Merge pull request #7391 from jumpserver/dev

v2.17.0 rc3
pull/7408/head
Jiangjie.Bai 2021-12-14 21:58:27 +08:00 committed by GitHub
commit 151d897746
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 155 additions and 126 deletions

View File

@ -138,7 +138,8 @@ class SystemUserAppAuthInfoApi(generics.RetrieveAPIView):
instance = super().get_object() instance = super().get_object()
app_id = self.kwargs.get('app_id') app_id = self.kwargs.get('app_id')
user_id = self.request.query_params.get("user_id") user_id = self.request.query_params.get("user_id")
instance.load_app_more_auth(app_id, user_id) username = self.request.query_params.get("username")
instance.load_app_more_auth(app_id, username, user_id)
return instance return instance

View File

@ -129,12 +129,21 @@ class AuthMixin:
if password: if password:
self.password = password self.password = password
def load_app_more_auth(self, app_id=None, user_id=None): def load_app_more_auth(self, app_id=None, username=None, user_id=None):
self._clean_auth_info_if_manual_login_mode() self._clean_auth_info_if_manual_login_mode()
# 加载临时认证信息 # 加载临时认证信息
if self.login_mode == self.LOGIN_MANUAL: if self.login_mode == self.LOGIN_MANUAL:
self._load_tmp_auth_if_has(app_id, user_id) self._load_tmp_auth_if_has(app_id, user_id)
return return
# 更新用户名
from users.models import User
user = get_object_or_none(User, pk=user_id) if user_id else None
if self.username_same_with_user:
if user and not username:
_username = user.username
else:
_username = username
self.username = _username
def load_asset_special_auth(self, asset, username=''): def load_asset_special_auth(self, asset, username=''):
""" """

View File

@ -40,10 +40,6 @@ def expire_node_assets_mapping_for_memory(org_id):
root_org_id = Organization.ROOT_ID root_org_id = Organization.ROOT_ID
# 当前进程清除(cache 数据) # 当前进程清除(cache 数据)
logger.debug(
"Expire node assets id mapping from cache of org={}, pid={}"
"".format(org_id, os.getpid())
)
Node.expire_node_all_asset_ids_mapping_from_cache(org_id) Node.expire_node_all_asset_ids_mapping_from_cache(org_id)
Node.expire_node_all_asset_ids_mapping_from_cache(root_org_id) Node.expire_node_all_asset_ids_mapping_from_cache(root_org_id)
@ -81,10 +77,6 @@ def subscribe_node_assets_mapping_expire(sender, **kwargs):
root_org_id = Organization.ROOT_ID root_org_id = Organization.ROOT_ID
Node.expire_node_all_asset_ids_mapping_from_memory(org_id) Node.expire_node_all_asset_ids_mapping_from_memory(org_id)
Node.expire_node_all_asset_ids_mapping_from_memory(root_org_id) Node.expire_node_all_asset_ids_mapping_from_memory(root_org_id)
logger.debug(
"Expire node assets id mapping from memory of org={}, pid={}"
"".format(str(org_id), os.getpid())
)
def keep_subscribe_node_assets_relation(): def keep_subscribe_node_assets_relation():
node_assets_mapping_for_memory_pub_sub.keep_handle_msg(handle_node_relation_change) node_assets_mapping_for_memory_pub_sub.keep_handle_msg(handle_node_relation_change)

View File

@ -13,16 +13,24 @@ __all__ = ['add_nodes_assets_to_system_users']
@tmp_to_root_org() @tmp_to_root_org()
def add_nodes_assets_to_system_users(nodes_keys, system_users): def add_nodes_assets_to_system_users(nodes_keys, system_users):
from ..models import Node from ..models import Node
from assets.tasks import push_system_user_to_assets
nodes = Node.objects.filter(key__in=nodes_keys) nodes = Node.objects.filter(key__in=nodes_keys)
assets = Node.get_nodes_all_assets(*nodes) assets = Node.get_nodes_all_assets(*nodes)
for system_user in system_users: for system_user in system_users:
""" 解决资产和节点进行关联时,已经关联过的节点不会触发 authbook post_save 信号, """ 解决资产和节点进行关联时,已经关联过的节点不会触发 authbook post_save 信号,
无法更新节点下所有资产的管理用户的问题 """ 无法更新节点下所有资产的管理用户的问题 """
need_push_asset_ids = []
for asset in assets: for asset in assets:
defaults = {'asset': asset, 'systemuser': system_user, 'org_id': asset.org_id} defaults = {'asset': asset, 'systemuser': system_user, 'org_id': asset.org_id}
instance, created = AuthBook.objects.update_or_create( instance, created = AuthBook.objects.update_or_create(
defaults=defaults, asset=asset, systemuser=system_user defaults=defaults, asset=asset, systemuser=system_user
) )
if created:
need_push_asset_ids.append(asset.id)
# # 不再自动更新资产管理用户,只允许用户手动指定。 # # 不再自动更新资产管理用户,只允许用户手动指定。
# 只要关联都需要更新资产的管理用户 # 只要关联都需要更新资产的管理用户
# instance.update_asset_admin_user_if_need() # instance.update_asset_admin_user_if_need()
if need_push_asset_ids:
push_system_user_to_assets.delay(system_user.id, need_push_asset_ids)

View File

@ -138,7 +138,7 @@ def get_push_unixlike_system_user_tasks(system_user, username=None):
return tasks return tasks
def get_push_windows_system_user_tasks(system_user, username=None): def get_push_windows_system_user_tasks(system_user: SystemUser, username=None):
if username is None: if username is None:
username = system_user.username username = system_user.username
password = system_user.password password = system_user.password
@ -151,6 +151,11 @@ def get_push_windows_system_user_tasks(system_user, username=None):
if not password: if not password:
logger.error("Error: no password found") logger.error("Error: no password found")
return tasks return tasks
if system_user.ad_domain:
logger.error('System user with AD domain do not support push.')
return tasks
task = { task = {
'name': 'Add user {}'.format(username), 'name': 'Add user {}'.format(username),
'action': { 'action': {

View File

@ -294,7 +294,7 @@ class SecretDetailMixin:
data.update(asset_detail) data.update(asset_detail)
else: else:
app_detail = self._get_application_secret_detail(app) app_detail = self._get_application_secret_detail(app)
system_user.load_app_more_auth(app.id, user.id) system_user.load_app_more_auth(app.id, user.username, user.id)
data['type'] = 'application' data['type'] = 'application'
data.update(app_detail) data.update(app_detail)

View File

@ -1,5 +1,7 @@
import copy import copy
from urllib import parse
from django.views import View from django.views import View
from django.contrib import auth as auth from django.contrib import auth as auth
from django.urls import reverse from django.urls import reverse
@ -23,9 +25,13 @@ logger = get_logger(__file__)
class PrepareRequestMixin: class PrepareRequestMixin:
@staticmethod @staticmethod
def prepare_django_request(request): def is_secure():
url_result = parse.urlparse(settings.SITE_URL)
return 'on' if url_result.scheme == 'https' else 'off'
def prepare_django_request(self, request):
result = { result = {
'https': 'on' if request.is_secure() else 'off', 'https': self.is_secure(),
'http_host': request.META['HTTP_HOST'], 'http_host': request.META['HTTP_HOST'],
'script_name': request.META['PATH_INFO'], 'script_name': request.META['PATH_INFO'],
'get_data': request.GET.copy(), 'get_data': request.GET.copy(),

View File

@ -173,7 +173,6 @@ class Cache(metaclass=CacheType):
def expire(self, *fields): def expire(self, *fields):
self._data = None self._data = None
if not fields: if not fields:
logger.debug(f'Delete cached key: key={self.key}')
self.redis.delete(self.key) self.redis.delete(self.key)
else: else:
self.redis.hdel(self.key, *fields) self.redis.hdel(self.key, *fields)

View File

@ -170,7 +170,7 @@ class BaseService(object):
def _restart(self): def _restart(self):
if self.retry > self.max_retry: if self.retry > self.max_retry:
logging.info("Service start failed, exit: ", self.name) logging.info("Service start failed, exit: {}".format(self.name))
self.EXIT_EVENT.set() self.EXIT_EVENT.set()
return return
self.retry += 1 self.retry += 1

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-09 20:32+0800\n" "POT-Creation-Date: 2021-12-14 17:54+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -343,7 +343,7 @@ msgstr "类别名称"
#: perms/serializers/application/permission.py:17 #: perms/serializers/application/permission.py:17
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:33 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:33
#: tickets/serializers/ticket/ticket.py:22 #: tickets/serializers/ticket/ticket.py:22
#: tickets/serializers/ticket/ticket.py:168 #: tickets/serializers/ticket/ticket.py:169
msgid "Type display" msgid "Type display"
msgstr "类型名称" msgstr "类型名称"
@ -384,10 +384,10 @@ msgstr "应用路径"
#: applications/serializers/attrs/application_category/remote_app.py:45 #: applications/serializers/attrs/application_category/remote_app.py:45
#: assets/serializers/system_user.py:159 #: assets/serializers/system_user.py:159
#: xpack/plugins/change_auth_plan/serializers/asset.py:65 #: xpack/plugins/change_auth_plan/serializers/asset.py:64
#: xpack/plugins/change_auth_plan/serializers/asset.py:68 #: xpack/plugins/change_auth_plan/serializers/asset.py:67
#: xpack/plugins/change_auth_plan/serializers/asset.py:71 #: xpack/plugins/change_auth_plan/serializers/asset.py:70
#: xpack/plugins/change_auth_plan/serializers/asset.py:87 #: xpack/plugins/change_auth_plan/serializers/asset.py:101
#: xpack/plugins/cloud/serializers/account_attrs.py:52 #: xpack/plugins/cloud/serializers/account_attrs.py:52
msgid "This field is required." msgid "This field is required."
msgstr "该字段是必填项。" msgstr "该字段是必填项。"
@ -586,6 +586,7 @@ msgid "Ok"
msgstr "成功" msgstr "成功"
#: assets/models/base.py:32 audits/models.py:102 #: assets/models/base.py:32 audits/models.py:102
#: xpack/plugins/change_auth_plan/task_handlers/base/manager.py:121
#: xpack/plugins/cloud/const.py:29 #: xpack/plugins/cloud/const.py:29
msgid "Failed" msgid "Failed"
msgstr "失败" msgstr "失败"
@ -1169,6 +1170,7 @@ msgid "Filename"
msgstr "文件名" msgstr "文件名"
#: audits/models.py:42 audits/models.py:101 terminal/models/sharing.py:84 #: audits/models.py:42 audits/models.py:101 terminal/models/sharing.py:84
#: xpack/plugins/change_auth_plan/task_handlers/base/manager.py:119
msgid "Success" msgid "Success"
msgstr "成功" msgstr "成功"
@ -1326,12 +1328,12 @@ msgstr ""
msgid "Auth Token" msgid "Auth Token"
msgstr "认证令牌" msgstr "认证令牌"
#: audits/signals_handler.py:68 authentication/views/login.py:183 #: audits/signals_handler.py:68 authentication/views/login.py:164
#: notifications/backends/__init__.py:11 users/models/user.py:607 #: notifications/backends/__init__.py:11 users/models/user.py:607
msgid "WeCom" msgid "WeCom"
msgstr "企业微信" msgstr "企业微信"
#: audits/signals_handler.py:69 authentication/views/login.py:189 #: audits/signals_handler.py:69 authentication/views/login.py:170
#: notifications/backends/__init__.py:12 users/models/user.py:608 #: notifications/backends/__init__.py:12 users/models/user.py:608
msgid "DingTalk" msgid "DingTalk"
msgstr "钉钉" msgstr "钉钉"
@ -1899,7 +1901,7 @@ msgstr "代码错误"
#: authentication/templates/authentication/_msg_reset_password.html:3 #: authentication/templates/authentication/_msg_reset_password.html:3
#: authentication/templates/authentication/_msg_rest_password_success.html:2 #: authentication/templates/authentication/_msg_rest_password_success.html:2
#: authentication/templates/authentication/_msg_rest_public_key_success.html:2 #: authentication/templates/authentication/_msg_rest_public_key_success.html:2
#: jumpserver/conf.py:282 #: jumpserver/conf.py:293
#: perms/templates/perms/_msg_item_permissions_expire.html:3 #: perms/templates/perms/_msg_item_permissions_expire.html:3
#: perms/templates/perms/_msg_permed_items_expire.html:3 #: perms/templates/perms/_msg_permed_items_expire.html:3
#: users/templates/users/_msg_account_expire_reminder.html:4 #: users/templates/users/_msg_account_expire_reminder.html:4
@ -2107,24 +2109,24 @@ msgstr "没有绑定飞书"
msgid "Please login with a password and then bind the FeiShu" msgid "Please login with a password and then bind the FeiShu"
msgstr "请使用密码登录,然后绑定飞书" msgstr "请使用密码登录,然后绑定飞书"
#: authentication/views/login.py:89 #: authentication/views/login.py:70
msgid "Redirecting" msgid "Redirecting"
msgstr "跳转中" msgstr "跳转中"
#: authentication/views/login.py:90 #: authentication/views/login.py:71
msgid "Redirecting to {} authentication" msgid "Redirecting to {} authentication"
msgstr "正在跳转到 {} 认证" msgstr "正在跳转到 {} 认证"
#: authentication/views/login.py:116 #: authentication/views/login.py:94
msgid "Please enable cookies and try again." msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie" msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:195 notifications/backends/__init__.py:14 #: authentication/views/login.py:176 notifications/backends/__init__.py:14
#: users/models/user.py:609 #: users/models/user.py:609
msgid "FeiShu" msgid "FeiShu"
msgstr "飞书" msgstr "飞书"
#: authentication/views/login.py:284 #: authentication/views/login.py:265
msgid "" msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n" "Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page" " Don't close this page"
@ -2132,15 +2134,15 @@ msgstr ""
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n" "等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
" 不要关闭本页面" " 不要关闭本页面"
#: authentication/views/login.py:289 #: authentication/views/login.py:270
msgid "No ticket found" msgid "No ticket found"
msgstr "没有发现工单" msgstr "没有发现工单"
#: authentication/views/login.py:323 #: authentication/views/login.py:304
msgid "Logout success" msgid "Logout success"
msgstr "退出登录成功" msgstr "退出登录成功"
#: authentication/views/login.py:324 #: authentication/views/login.py:305
msgid "Logout success, return login page" msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面" msgstr "退出登录成功,返回到登录页面"
@ -2339,11 +2341,11 @@ msgstr "不能包含特殊字符"
msgid "The mobile phone number format is incorrect" msgid "The mobile phone number format is incorrect"
msgstr "手机号格式不正确" msgstr "手机号格式不正确"
#: jumpserver/conf.py:281 #: jumpserver/conf.py:292
msgid "Create account successfully" msgid "Create account successfully"
msgstr "创建账户成功" msgstr "创建账户成功"
#: jumpserver/conf.py:283 #: jumpserver/conf.py:294
msgid "Your account has been created successfully" msgid "Your account has been created successfully"
msgstr "你的账户已创建成功" msgstr "你的账户已创建成功"
@ -2859,7 +2861,7 @@ msgstr "服务端地址"
msgid "Proxy server url" msgid "Proxy server url"
msgstr "回调地址" msgstr "回调地址"
#: settings/serializers/auth/cas.py:14 settings/serializers/auth/saml2.py:29 #: settings/serializers/auth/cas.py:14 settings/serializers/auth/saml2.py:32
msgid "Logout completely" msgid "Logout completely"
msgstr "同步注销" msgstr "同步注销"
@ -2871,7 +2873,7 @@ msgstr "用户名属性"
msgid "Enable attributes map" msgid "Enable attributes map"
msgstr "启用属性映射" msgstr "启用属性映射"
#: settings/serializers/auth/cas.py:18 settings/serializers/auth/saml2.py:28 #: settings/serializers/auth/cas.py:18 settings/serializers/auth/saml2.py:31
msgid "Rename attr" msgid "Rename attr"
msgstr "映射属性" msgstr "映射属性"
@ -3031,7 +3033,7 @@ msgstr "使用状态"
msgid "Use nonce" msgid "Use nonce"
msgstr "临时使用" msgstr "临时使用"
#: settings/serializers/auth/oidc.py:76 settings/serializers/auth/saml2.py:30 #: settings/serializers/auth/oidc.py:76 settings/serializers/auth/saml2.py:33
msgid "Always update user" msgid "Always update user"
msgstr "总是更新用户信息" msgstr "总是更新用户信息"
@ -3048,20 +3050,24 @@ msgid "Enable SAML2 Auth"
msgstr "启用 SAML2 认证" msgstr "启用 SAML2 认证"
#: settings/serializers/auth/saml2.py:15 #: settings/serializers/auth/saml2.py:15
msgid "IDP Metadata URL" msgid "IDP metadata URL"
msgstr "" msgstr ""
#: settings/serializers/auth/saml2.py:18 #: settings/serializers/auth/saml2.py:18
msgid "IDP Metadata XML" msgid "IDP metadata XML"
msgstr "" msgstr ""
#: settings/serializers/auth/saml2.py:22 #: settings/serializers/auth/saml2.py:21
msgid "SP Private Key" msgid "SP advanced settings"
msgstr "" msgstr "高级设置"
#: settings/serializers/auth/saml2.py:26 #: settings/serializers/auth/saml2.py:25
msgid "SP Public Cert" msgid "SP private key"
msgstr "" msgstr "SP 密钥"
#: settings/serializers/auth/saml2.py:29
msgid "SP cert"
msgstr "SP 证书"
#: settings/serializers/auth/sms.py:10 #: settings/serializers/auth/sms.py:10
msgid "Enable SMS" msgid "Enable SMS"
@ -4784,11 +4790,11 @@ msgstr "内容"
msgid "Approve level" msgid "Approve level"
msgstr "审批级别" msgstr "审批级别"
#: tickets/models/flow.py:25 tickets/serializers/ticket/ticket.py:140 #: tickets/models/flow.py:25 tickets/serializers/ticket/ticket.py:141
msgid "Approve strategy" msgid "Approve strategy"
msgstr "审批策略" msgstr "审批策略"
#: tickets/models/flow.py:30 tickets/serializers/ticket/ticket.py:141 #: tickets/models/flow.py:30 tickets/serializers/ticket/ticket.py:142
msgid "Assignees" msgid "Assignees"
msgstr "受理人" msgstr "受理人"
@ -4876,7 +4882,7 @@ msgstr "申请的系统用户名称"
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:71 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:71
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:73 #: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:73
#: tickets/serializers/ticket/ticket.py:127 #: tickets/serializers/ticket/ticket.py:128
msgid "Permission named `{}` already exists" msgid "Permission named `{}` already exists"
msgstr "授权名称 `{}` 已存在" msgstr "授权名称 `{}` 已存在"
@ -4936,7 +4942,7 @@ msgid "From cmd filter"
msgstr "来自命令过滤规则" msgstr "来自命令过滤规则"
#: tickets/serializers/ticket/meta/ticket_type/common.py:11 #: tickets/serializers/ticket/meta/ticket_type/common.py:11
#: tickets/serializers/ticket/ticket.py:122 #: tickets/serializers/ticket/ticket.py:123
msgid "Created by ticket ({}-{})" msgid "Created by ticket ({}-{})"
msgstr "通过工单创建 ({}-{})" msgstr "通过工单创建 ({}-{})"
@ -4962,15 +4968,15 @@ msgid ""
"request url (`{}`)" "request url (`{}`)"
msgstr "提交数据中的类型 (`{}`) 与请求URL地址中的类型 (`{}`) 不一致" msgstr "提交数据中的类型 (`{}`) 与请求URL地址中的类型 (`{}`) 不一致"
#: tickets/serializers/ticket/ticket.py:115 #: tickets/serializers/ticket/ticket.py:116
msgid "The ticket flow `{}` does not exist" msgid "The ticket flow `{}` does not exist"
msgstr "工单流程 `{}` 不存在" msgstr "工单流程 `{}` 不存在"
#: tickets/serializers/ticket/ticket.py:162 #: tickets/serializers/ticket/ticket.py:163
msgid "Please select the Assignees" msgid "Please select the Assignees"
msgstr "请选择受理人" msgstr "请选择受理人"
#: tickets/serializers/ticket/ticket.py:188 #: tickets/serializers/ticket/ticket.py:189
msgid "The current organization type already exists" msgid "The current organization type already exists"
msgstr "当前组织已存在该类型" msgstr "当前组织已存在该类型"
@ -5633,7 +5639,7 @@ msgid "Replace (The key generated by JumpServer) "
msgstr "替换 (由 JumpServer 生成的密钥)" msgstr "替换 (由 JumpServer 生成的密钥)"
#: xpack/plugins/change_auth_plan/models/asset.py:50 #: xpack/plugins/change_auth_plan/models/asset.py:50
#: xpack/plugins/change_auth_plan/serializers/asset.py:34 #: xpack/plugins/change_auth_plan/serializers/asset.py:33
msgid "SSH Key strategy" msgid "SSH Key strategy"
msgstr "SSH 密钥策略" msgstr "SSH 密钥策略"
@ -5722,11 +5728,11 @@ msgstr ""
"{} - 改密任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设置加" "{} - 改密任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设置加"
"密密码" "密密码"
#: xpack/plugins/change_auth_plan/serializers/asset.py:31 #: xpack/plugins/change_auth_plan/serializers/asset.py:30
msgid "Change Password" msgid "Change Password"
msgstr "更改密码" msgstr "更改密码"
#: xpack/plugins/change_auth_plan/serializers/asset.py:32 #: xpack/plugins/change_auth_plan/serializers/asset.py:31
msgid "Change SSH Key" msgid "Change SSH Key"
msgstr "修改 SSH Key" msgstr "修改 SSH Key"
@ -5811,8 +5817,8 @@ msgid "Qingyun Private Cloud"
msgstr "青云私有云" msgstr "青云私有云"
#: xpack/plugins/cloud/const.py:19 #: xpack/plugins/cloud/const.py:19
msgid "OpenStack Cloud" msgid "OpenStack"
msgstr "OpenStack Cloud" msgstr "OpenStack"
#: xpack/plugins/cloud/const.py:20 #: xpack/plugins/cloud/const.py:20
msgid "Google Cloud Platform" msgid "Google Cloud Platform"

View File

@ -6,6 +6,7 @@ from rest_framework import generics
from rest_framework.views import Response, APIView from rest_framework.views import Response, APIView
from orgs.models import Organization from orgs.models import Organization
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from ..utils import ( from ..utils import (
LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil, LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil,
@ -47,6 +48,10 @@ class LDAPTestingConfigAPI(APIView):
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"] search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"] attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
auth_ldap = serializer.validated_data.get('AUTH_LDAP', False) auth_ldap = serializer.validated_data.get('AUTH_LDAP', False)
if not password:
password = settings.AUTH_LDAP_BIND_PASSWORD
config = { config = {
'server_uri': server_uri, 'server_uri': server_uri,
'bind_dn': bind_dn, 'bind_dn': bind_dn,

View File

@ -12,21 +12,21 @@ class SAML2SettingSerializer(serializers.Serializer):
default=False, required=False, label=_('Enable SAML2 Auth') default=False, required=False, label=_('Enable SAML2 Auth')
) )
SAML2_IDP_METADATA_URL = serializers.URLField( SAML2_IDP_METADATA_URL = serializers.URLField(
allow_blank=True, required=False, label=_('IDP Metadata URL') allow_blank=True, required=False, label=_('IDP metadata URL')
) )
SAML2_IDP_METADATA_XML = serializers.CharField( SAML2_IDP_METADATA_XML = serializers.CharField(
allow_blank=True, required=False, label=_('IDP Metadata XML') allow_blank=True, required=False, label=_('IDP metadata XML')
) )
SAML2_SP_ADVANCED_SETTINGS = serializers.JSONField( SAML2_SP_ADVANCED_SETTINGS = serializers.JSONField(
required=False, label=_('SP ADVANCED SETTINGS') required=False, label=_('SP advanced settings')
) )
SAML2_SP_KEY_CONTENT = serializers.CharField( SAML2_SP_KEY_CONTENT = serializers.CharField(
allow_blank=True, required=False, allow_blank=True, required=False,
write_only=True, label=_('SP Private Key') write_only=True, label=_('SP private key')
) )
SAML2_SP_CERT_CONTENT = serializers.CharField( SAML2_SP_CERT_CONTENT = serializers.CharField(
allow_blank=True, required=False, allow_blank=True, required=False,
write_only=True, label=_('SP Public Cert') write_only=True, label=_('SP cert')
) )
SAML2_RENAME_ATTRIBUTES = serializers.DictField(required=False, label=_('Rename attr')) SAML2_RENAME_ATTRIBUTES = serializers.DictField(required=False, label=_('Rename attr'))
SAML2_LOGOUT_COMPLETELY = serializers.BooleanField(required=False, label=_('Logout completely')) SAML2_LOGOUT_COMPLETELY = serializers.BooleanField(required=False, label=_('Logout completely'))

View File

@ -39,7 +39,8 @@ class UserOtpEnableStartView(AuthMixin, TemplateView):
try: try:
self.get_user_from_session() self.get_user_from_session()
except SessionEmptyError: except SessionEmptyError:
return redirect('authentication:login') + '?_=otp_enable_start' url = reverse('authentication:login') + '?_=otp_enable_start'
return redirect(url)
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
@ -72,8 +73,8 @@ class UserOtpEnableBindView(AuthMixin, TemplateView, FormView):
def _pre_check_can_bind(self): def _pre_check_can_bind(self):
try: try:
user = self.get_user_from_session() user = self.get_user_from_session()
except: except Exception as e:
verify_url = reverse('authentication:user-otp-enable-start') verify_url = reverse('authentication:user-otp-enable-start') + f'?e={e}'
return HttpResponseRedirect(verify_url) return HttpResponseRedirect(verify_url)
if user.otp_secret_key: if user.otp_secret_key:

111
jms
View File

@ -2,20 +2,27 @@
# coding: utf-8 # coding: utf-8
import os import os
import subprocess
import logging import logging
import logging.handlers import logging.handlers
import time import time
import argparse import argparse
import sys import sys
import django
from django.core import management
from django.db.utils import OperationalError
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, BASE_DIR) APP_DIR = os.path.join(BASE_DIR, 'apps')
os.chdir(APP_DIR)
sys.path.insert(0, APP_DIR)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
django.setup()
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
try: try:
from apps.jumpserver import const from jumpserver import const
__version__ = const.VERSION __version__ = const.VERSION
except ImportError as e: except ImportError as e:
print("Not found __version__: {}".format(e)) print("Not found __version__: {}".format(e))
@ -25,7 +32,7 @@ except ImportError as e:
sys.exit(1) sys.exit(1)
try: try:
from apps.jumpserver.const import CONFIG from jumpserver.const import CONFIG
except ImportError as e: except ImportError as e:
print("Import error: {}".format(e)) print("Import error: {}".format(e))
print("Could not find config file, `cp config_example.yml config.yml`") print("Could not find config file, `cp config_example.yml config.yml`")
@ -48,56 +55,45 @@ except:
def check_database_connection(): def check_database_connection():
os.chdir(os.path.join(BASE_DIR, 'apps'))
for i in range(60): for i in range(60):
logging.info("Check database connection ...") logging.info(f"Check database connection: {i}")
_code = subprocess.call("python manage.py showmigrations users ", shell=True) try:
if _code == 0: management.call_command('check', '--database', 'default')
logging.info("Database connect success") logging.info("Database connect success")
return return
except OperationalError:
logging.info('Database not setup, retry')
except Exception as e:
logging.error('Unexpect error occur: {}'.format(str(e)))
time.sleep(1) time.sleep(1)
logging.error("Connection database failed, exit") logging.error("Connection database failed, exit")
sys.exit(10) sys.exit(10)
def check_migrations():
_apps_dir = os.path.join(BASE_DIR, 'apps')
_cmd = "python manage.py showmigrations | grep '\[.\]' | grep -v '\[X\]'"
_code = subprocess.call(_cmd, shell=True, cwd=_apps_dir)
if _code == 1:
return
# for i in range(3):
# print("!!! Warning: Has SQL migrations not perform, 有 SQL 变更没有执行")
# print("You should run `./PROC upgrade_db` first, 请先运行 ./PROC upgrade_db, 进行表结构变更")
# sys.exit(1)
def expire_caches(): def expire_caches():
_apps_dir = os.path.join(BASE_DIR, 'apps') try:
_code = subprocess.call("python manage.py expire_caches", shell=True, cwd=_apps_dir) management.call_command('expire_caches')
except:
if _code == 1: pass
return
def perform_db_migrate(): def perform_db_migrate():
logging.info("Check database structure change ...") logging.info("Check database structure change ...")
os.chdir(os.path.join(BASE_DIR, 'apps'))
logging.info("Migrate model change to database ...") logging.info("Migrate model change to database ...")
_code = subprocess.call('python3 manage.py migrate', shell=True) try:
if _code == 0: management.call_command('migrate')
return except Exception:
logging.error('Perform migrate failed, exit') logging.error('Perform migrate failed, exit')
sys.exit(11) sys.exit(11)
def collect_static(): def collect_static():
logging.info("Collect static files") logging.info("Collect static files")
os.chdir(os.path.join(BASE_DIR, 'apps')) try:
_cmd = 'python3 manage.py collectstatic --no-input -c &> /dev/null ' management.call_command('collectstatic', '--no-input', '-c', verbosity=0, interactive=False)
subprocess.call(_cmd, shell=True)
logging.info("Collect static files done") logging.info("Collect static files done")
except:
pass
def compile_i81n_file(): def compile_i81n_file():
@ -105,8 +101,7 @@ def compile_i81n_file():
if os.path.exists(django_mo_file): if os.path.exists(django_mo_file):
return return
os.chdir(os.path.join(BASE_DIR, 'apps')) os.chdir(os.path.join(BASE_DIR, 'apps'))
_cmd = 'python3 manage.py compilemessages --no-input -c &> /dev/null ' management.call_command('compilemessages', verbosity=0, interactive=False)
subprocess.call(_cmd, shell=True)
logging.info("Compile i18n files done") logging.info("Compile i18n files done")
@ -116,13 +111,34 @@ def upgrade_db():
def prepare(): def prepare():
# installer(check) & k8s(no check)
check_database_connection() check_database_connection()
check_migrations()
upgrade_db() upgrade_db()
expire_caches() expire_caches()
def start_services():
services = args.services if isinstance(args.services, list) else [args.services]
if action == 'start' and {'all', 'web'} & set(services):
prepare()
start_args = []
if args.daemon:
start_args.append('--daemon')
if args.worker:
start_args.extend(['--worker', str(args.worker)])
if args.force:
start_args.append('--force')
try:
management.call_command(action, *services, *start_args)
except KeyboardInterrupt:
logging.info('Cancel ...')
time.sleep(2)
except Exception as e:
logging.error("Start service error {}: {}".format(services, e))
time.sleep(2)
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description=""" description="""
@ -155,23 +171,4 @@ if __name__ == '__main__':
elif action == "collect_static": elif action == "collect_static":
collect_static() collect_static()
else: else:
services = args.services if isinstance(args.services, list) else [args.services] start_services()
if action == 'start' and {'all', 'web'} & set(services):
prepare()
services_string = ' '.join(services)
cmd = f'python manage.py {args.action} {services_string}'
if args.daemon:
cmd += ' --daemon'
if args.worker:
cmd += f' --worker {args.worker}'
if args.force:
cmd += ' --force'
apps_dir = os.path.join(BASE_DIR, 'apps')
try:
# processes: main(3s) -> call(0.25s) -> service -> sub-process
code = subprocess.call(cmd, shell=True, cwd=apps_dir)
except KeyboardInterrupt:
time.sleep(2)
pass