[Update] Merge branch dev to 1.5.5

pull/3428/head
BaiJiangJie 2019-11-12 14:11:34 +08:00
commit 5434b65773
19 changed files with 831 additions and 505 deletions

View File

@ -6,6 +6,7 @@ import time
from django.db import models, transaction
from django.db.models import Q
from django.db.utils import IntegrityError
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from django.core.cache import cache
@ -67,7 +68,7 @@ class TreeMixin:
@classmethod
def refresh_node_assets(cls, t=None):
logger.debug("Refresh node tree assets")
logger.debug("Refresh node assets")
key = cls.tree_assets_cache_key
ttl = cls.tree_cache_time
if not t:
@ -336,6 +337,17 @@ class SomeNodesMixin:
else:
return False
@classmethod
def get_next_org_root_node_key(cls):
with tmp_to_org(Organization.root()):
org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True)
if not org_nodes_roots_keys:
org_nodes_roots_keys = ['1']
max_key = max([int(k) for k in org_nodes_roots_keys])
key = str(max_key + 1) if max_key != 0 else '2'
return key
@classmethod
def create_org_root_node(cls):
# 如果使用current_org 在set_current_org时会死循环
@ -343,14 +355,7 @@ class SomeNodesMixin:
with transaction.atomic():
if not ori_org.is_real():
return cls.default_node()
set_current_org(Organization.root())
org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True)
if not org_nodes_roots_keys:
org_nodes_roots_keys = ['1']
key = max([int(k) for k in org_nodes_roots_keys])
key = str(key + 1) if key != 0 else '2'
set_current_org(ori_org)
key = cls.get_next_org_root_node_key()
root = cls.objects.create(key=key, value=ori_org.name)
return root
@ -384,9 +389,16 @@ class SomeNodesMixin:
def default_node(cls):
with tmp_to_org(Organization.default()):
defaults = {'value': cls.default_value}
obj, created = cls.objects.get_or_create(
defaults=defaults, key=cls.default_key,
)
try:
obj, created = cls.objects.get_or_create(
defaults=defaults, key=cls.default_key,
)
except IntegrityError as e:
logger.error("Create default node failed: {}".format(e))
cls.modify_other_org_root_node_key()
obj, created = cls.objects.get_or_create(
defaults=defaults, key=cls.default_key,
)
return obj
@classmethod
@ -405,6 +417,35 @@ class SomeNodesMixin:
cls.ungrouped_node()
cls.favorite_node()
@classmethod
def modify_other_org_root_node_key(cls):
"""
解决创建 default 节点失败的问题
因为在其他组织下存在 default 节点故在 DEFAULT 组织下 get 不到 create 失败
"""
logger.info("Modify other org root node key")
with tmp_to_org(Organization.root()):
node_key1 = cls.objects.filter(key='1').first()
if not node_key1:
logger.info("Not found node that `key` = 1")
return
if not node_key1.org.is_real():
logger.info("Org is not real for node that `key` = 1")
return
with transaction.atomic():
with tmp_to_org(node_key1.org):
org_root_node_new_key = cls.get_next_org_root_node_key()
for n in cls.objects.all():
old_key = n.key
key_list = n.key.split(':')
key_list[0] = org_root_node_new_key
new_key = ':'.join(key_list)
n.key = new_key
n.save()
logger.info('Modify key ( {} > {} )'.format(old_key, new_key))
class Node(OrgModelMixin, SomeNodesMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)

View File

@ -426,13 +426,15 @@ $(document).ready(function(){
function success(data) {
url = setUrlParam(the_url, 'spm', data.spm);
requestApi({
url:url,
method:'DELETE',
success:refreshPage,
flash_message:false,
url: url,
method: 'DELETE',
success: function () {
var msg = "{% trans 'Asset Deleted.' %}";
swal("{% trans 'Asset Delete' %}", msg, "success");
refreshPage();
},
flash_message: false,
});
var msg = "{% trans 'Asset Deleted.' %}";
swal("{% trans 'Asset Delete' %}", msg, "success");
}
function fail() {
var msg = "{% trans 'Asset Deleting failed.' %}";
@ -440,10 +442,11 @@ $(document).ready(function(){
}
requestApi({
url: "{% url 'api-common:resources-cache' %}",
method:'POST',
body:JSON.stringify(data),
success:success,
error:fail
method: 'POST',
body: JSON.stringify(data),
success: success,
error: fail,
flash_message: false
})
})
}

View File

@ -43,7 +43,7 @@ def on_openid_login_success(sender, user=None, request=None, **kwargs):
@receiver(populate_user)
def on_ldap_create_user(sender, user, ldap_user, **kwargs):
if user and user.username != 'admin':
if user and user.username not in ['admin']:
user.source = user.SOURCE_LDAP
user.save()

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-08 17:27+0800\n"
"POT-Creation-Date: 2019-11-12 14:10+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
@ -96,7 +96,7 @@ msgstr "运行参数"
#: terminal/templates/terminal/session_list.html:28
#: terminal/templates/terminal/session_list.html:72
#: xpack/plugins/change_auth_plan/forms.py:73
#: xpack/plugins/change_auth_plan/models.py:412
#: xpack/plugins/change_auth_plan/models.py:419
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13
@ -137,14 +137,14 @@ msgstr "资产"
#: perms/templates/perms/remote_app_permission_remote_app.html:53
#: perms/templates/perms/remote_app_permission_user.html:53
#: settings/models.py:29
#: settings/templates/settings/_ldap_list_users_modal.html:31
#: settings/templates/settings/_ldap_list_users_modal.html:32
#: settings/templates/settings/command_storage_create.html:41
#: settings/templates/settings/replay_storage_create.html:44
#: settings/templates/settings/terminal_setting.html:83
#: settings/templates/settings/terminal_setting.html:105 terminal/models.py:23
#: terminal/models.py:260 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:382 users/templates/users/_select_user_modal.html:13
#: users/models/user.py:410 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:64
#: users/templates/users/user_group_detail.html:55
#: users/templates/users/user_group_list.html:35
@ -152,7 +152,7 @@ msgstr "资产"
#: users/templates/users/user_profile.html:51
#: users/templates/users/user_pubkey_update.html:57
#: xpack/plugins/change_auth_plan/forms.py:56
#: xpack/plugins/change_auth_plan/models.py:63
#: xpack/plugins/change_auth_plan/models.py:64
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
#: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144
@ -197,9 +197,9 @@ msgstr "参数"
#: orgs/models.py:16 perms/models/base.py:54
#: perms/templates/perms/asset_permission_detail.html:98
#: perms/templates/perms/remote_app_permission_detail.html:90
#: users/models/user.py:423 users/serializers/group.py:32
#: users/models/user.py:451 users/serializers/group.py:32
#: users/templates/users/user_detail.html:112
#: xpack/plugins/change_auth_plan/models.py:108
#: xpack/plugins/change_auth_plan/models.py:109
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
#: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179
#: xpack/plugins/gathered_user/models.py:46
@ -260,11 +260,11 @@ msgstr "创建日期"
#: settings/models.py:34 terminal/models.py:33
#: terminal/templates/terminal/terminal_detail.html:63
#: tickets/templates/tickets/ticket_detail.html:106 users/models/group.py:15
#: users/models/user.py:415 users/templates/users/user_detail.html:130
#: users/models/user.py:443 users/templates/users/user_detail.html:130
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:37
#: users/templates/users/user_profile.html:138
#: xpack/plugins/change_auth_plan/models.py:104
#: xpack/plugins/change_auth_plan/models.py:105
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
#: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173
@ -529,7 +529,7 @@ msgstr "创建远程应用"
#: terminal/templates/terminal/session_list.html:36
#: terminal/templates/terminal/terminal_list.html:36
#: tickets/templates/tickets/login_confirm_ticket_list.html:18
#: tickets/templates/tickets/login_confirm_ticket_list.html:105
#: tickets/templates/tickets/login_confirm_ticket_list.html:106
#: users/templates/users/_granted_assets.html:34
#: users/templates/users/user_group_list.html:38
#: users/templates/users/user_list.html:41
@ -607,7 +607,7 @@ msgstr "端口"
#: assets/templates/assets/asset_detail.html:196
#: assets/templates/assets/system_user_assets.html:83
#: perms/models/asset_permission.py:81
#: xpack/plugins/change_auth_plan/models.py:74
#: xpack/plugins/change_auth_plan/models.py:75
#: xpack/plugins/gathered_user/models.py:31
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:17
msgid "Nodes"
@ -639,7 +639,7 @@ msgid "Domain"
msgstr "网域"
#: assets/forms/asset.py:69 assets/forms/asset.py:103 assets/forms/asset.py:116
#: assets/forms/asset.py:152 assets/models/node.py:421
#: assets/forms/asset.py:152 assets/models/node.py:462
#: assets/serializers/system_user.py:36
#: assets/templates/assets/asset_create.html:42
#: perms/forms/asset_permission.py:87 perms/forms/asset_permission.py:94
@ -704,18 +704,18 @@ msgstr "SSH网关支持代理SSH,RDP和VNC"
#: assets/templates/assets/system_user_list.html:48 audits/models.py:81
#: audits/templates/audits/login_log_list.html:57 authentication/forms.py:13
#: authentication/templates/authentication/login.html:58
#: authentication/templates/authentication/xpack_login.html:91
#: authentication/templates/authentication/xpack_login.html:93
#: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:70
#: perms/templates/perms/asset_permission_user.html:55
#: perms/templates/perms/remote_app_permission_user.html:54
#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:14
#: users/models/user.py:380 users/templates/users/_select_user_modal.html:14
#: settings/templates/settings/_ldap_list_users_modal.html:31 users/forms.py:14
#: users/models/user.py:408 users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:68
#: users/templates/users/user_list.html:36
#: users/templates/users/user_profile.html:47
#: xpack/plugins/change_auth_plan/forms.py:58
#: xpack/plugins/change_auth_plan/models.py:65
#: xpack/plugins/change_auth_plan/models.py:408
#: xpack/plugins/change_auth_plan/models.py:66
#: xpack/plugins/change_auth_plan/models.py:415
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12
@ -734,7 +734,7 @@ msgstr "密码或密钥密码"
#: assets/templates/assets/_asset_user_auth_view_modal.html:27
#: authentication/forms.py:15
#: authentication/templates/authentication/login.html:66
#: authentication/templates/authentication/xpack_login.html:99
#: authentication/templates/authentication/xpack_login.html:101
#: settings/forms.py:114 users/forms.py:16 users/forms.py:42
#: users/templates/users/reset_password.html:53
#: users/templates/users/user_password_authentication.html:18
@ -742,14 +742,14 @@ msgstr "密码或密钥密码"
#: users/templates/users/user_profile_update.html:41
#: users/templates/users/user_pubkey_update.html:41
#: users/templates/users/user_update.html:20
#: xpack/plugins/change_auth_plan/models.py:95
#: xpack/plugins/change_auth_plan/models.py:263
#: xpack/plugins/change_auth_plan/models.py:96
#: xpack/plugins/change_auth_plan/models.py:264
msgid "Password"
msgstr "密码"
#: assets/forms/user.py:30 assets/serializers/asset_user.py:71
#: assets/templates/assets/_asset_user_auth_update_modal.html:27
#: users/models/user.py:409
#: users/models/user.py:437
msgid "Private key"
msgstr "ssh私钥"
@ -937,13 +937,13 @@ msgstr "版本"
msgid "AuthBook"
msgstr ""
#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:99
#: xpack/plugins/change_auth_plan/models.py:270
#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:100
#: xpack/plugins/change_auth_plan/models.py:271
msgid "SSH private key"
msgstr "ssh密钥"
#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:102
#: xpack/plugins/change_auth_plan/models.py:266
#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:103
#: xpack/plugins/change_auth_plan/models.py:267
msgid "SSH public key"
msgstr "ssh公钥"
@ -963,7 +963,7 @@ msgstr "带宽"
msgid "Contact"
msgstr "联系人"
#: assets/models/cluster.py:22 users/models/user.py:401
#: assets/models/cluster.py:22 users/models/user.py:429
#: users/templates/users/user_detail.html:77
msgid "Phone"
msgstr "手机"
@ -989,7 +989,7 @@ msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:14
#: users/models/user.py:521
#: users/models/user.py:549
msgid "System"
msgstr "系统"
@ -1120,8 +1120,9 @@ msgstr "默认资产组"
#: terminal/templates/terminal/session_list.html:71 tickets/models/base.py:25
#: tickets/models/base.py:68
#: tickets/templates/tickets/login_confirm_ticket_list.html:15
#: tickets/templates/tickets/login_confirm_ticket_list.html:101
#: tickets/templates/tickets/ticket_detail.html:32 users/forms.py:339
#: users/models/user.py:132 users/models/user.py:148 users/models/user.py:509
#: users/models/user.py:149 users/models/user.py:165 users/models/user.py:537
#: users/serializers/group.py:21
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:36 users/views/user.py:250
@ -1131,7 +1132,7 @@ msgstr "默认资产组"
msgid "User"
msgstr "用户"
#: assets/models/label.py:19 assets/models/node.py:412
#: assets/models/label.py:19 assets/models/node.py:453
#: assets/templates/assets/label_list.html:15 settings/models.py:30
msgid "Value"
msgstr "值"
@ -1140,23 +1141,23 @@ msgstr "值"
msgid "Category"
msgstr "分类"
#: assets/models/node.py:163
#: assets/models/node.py:164
msgid "New node"
msgstr "新节点"
#: assets/models/node.py:324
#: assets/models/node.py:325
msgid "ungrouped"
msgstr "未分组"
#: assets/models/node.py:326
#: assets/models/node.py:327
msgid "empty"
msgstr "空"
#: assets/models/node.py:328
#: assets/models/node.py:329
msgid "favorite"
msgstr "收藏夹"
#: assets/models/node.py:411
#: assets/models/node.py:452
msgid "Key"
msgstr "键"
@ -1187,7 +1188,7 @@ msgstr "手动登录"
#: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73
#: assets/views/system_user.py:29 assets/views/system_user.py:46
#: assets/views/system_user.py:63 assets/views/system_user.py:79
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:70
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:71
msgid "Assets"
msgstr "资产管理"
@ -1277,7 +1278,7 @@ msgid "Backend"
msgstr "后端"
#: assets/serializers/asset_user.py:67 users/forms.py:282
#: users/models/user.py:412 users/templates/users/first_login.html:42
#: users/models/user.py:440 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:49
#: users/templates/users/user_profile.html:69
#: users/templates/users/user_profile_update.html:46
@ -1332,7 +1333,7 @@ msgstr "测试资产可连接性: {}"
#: assets/tasks/asset_user_connectivity.py:27
#: assets/tasks/push_system_user.py:130
#: xpack/plugins/change_auth_plan/models.py:521
#: xpack/plugins/change_auth_plan/models.py:528
msgid "The asset {} system platform {} does not support run Ansible tasks"
msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务"
@ -1450,6 +1451,7 @@ msgstr "资产列表"
#: assets/templates/assets/_node_tree.html:39
#: ops/templates/ops/command_execution_create.html:70
#: ops/templates/ops/command_execution_create.html:127
#: settings/templates/settings/_ldap_list_users_modal.html:41
#: users/templates/users/_granted_assets.html:7
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:66
msgid "Loading"
@ -1481,7 +1483,7 @@ msgid "Asset user auth"
msgstr "资产用户信息"
#: assets/templates/assets/_asset_user_auth_view_modal.html:54
#: authentication/templates/authentication/login_wait_confirm.html:115
#: authentication/templates/authentication/login_wait_confirm.html:114
msgid "Copy success"
msgstr "复制成功"
@ -1493,7 +1495,7 @@ msgstr "获取认证信息错误"
#: assets/templates/assets/_user_asset_detail_modal.html:23
#: authentication/templates/authentication/_access_key_modal.html:142
#: authentication/templates/authentication/_mfa_confirm_modal.html:53
#: settings/templates/settings/_ldap_list_users_modal.html:92
#: settings/templates/settings/_ldap_list_users_modal.html:171
#: templates/_modal.html:22 tickets/models/base.py:50
#: tickets/templates/tickets/ticket_detail.html:103
msgid "Close"
@ -1667,7 +1669,6 @@ msgstr "选择节点"
#: assets/templates/assets/system_user_detail.html:182
#: assets/templates/assets/system_user_list.html:135
#: authentication/templates/authentication/_mfa_confirm_modal.html:20
#: authentication/templates/authentication/login_wait_confirm.html:136
#: settings/templates/settings/terminal_setting.html:168
#: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:112
#: users/templates/users/user_detail.html:271
@ -1713,7 +1714,7 @@ msgstr "导出"
#: assets/templates/assets/admin_user_list.html:21
#: assets/templates/assets/asset_list.html:73
#: assets/templates/assets/system_user_list.html:24
#: settings/templates/settings/_ldap_list_users_modal.html:93
#: settings/templates/settings/_ldap_list_users_modal.html:172
#: users/templates/users/user_group_list.html:15
#: users/templates/users/user_list.html:15
#: xpack/plugins/license/templates/license/license_detail.html:110
@ -1899,16 +1900,16 @@ msgstr "删除选择资产"
msgid "Cancel"
msgstr "取消"
#: assets/templates/assets/asset_list.html:434
#: assets/templates/assets/asset_list.html:432
msgid "Asset Deleted."
msgstr "已被删除"
#: assets/templates/assets/asset_list.html:435
#: assets/templates/assets/asset_list.html:439
#: assets/templates/assets/asset_list.html:433
#: assets/templates/assets/asset_list.html:441
msgid "Asset Delete"
msgstr "删除"
#: assets/templates/assets/asset_list.html:438
#: assets/templates/assets/asset_list.html:440
msgid "Asset Deleting failed."
msgstr "删除失败"
@ -2281,13 +2282,13 @@ msgstr "Agent"
#: audits/models.py:86 audits/templates/audits/login_log_list.html:62
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms.py:194 users/models/user.py:404
#: users/forms.py:194 users/models/user.py:432
#: users/templates/users/first_login.html:45
msgid "MFA"
msgstr "MFA"
#: audits/models.py:87 audits/templates/audits/login_log_list.html:63
#: xpack/plugins/change_auth_plan/models.py:416
#: xpack/plugins/change_auth_plan/models.py:423
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15
#: xpack/plugins/cloud/models.py:278
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69
@ -2296,7 +2297,7 @@ msgstr "原因"
#: audits/models.py:88 audits/templates/audits/login_log_list.html:64
#: tickets/templates/tickets/login_confirm_ticket_list.html:16
#: tickets/templates/tickets/login_confirm_ticket_list.html:101
#: tickets/templates/tickets/login_confirm_ticket_list.html:102
#: tickets/templates/tickets/ticket_detail.html:34
#: xpack/plugins/cloud/models.py:275 xpack/plugins/cloud/models.py:310
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70
@ -2316,8 +2317,8 @@ msgstr "登录日期"
#: perms/templates/perms/asset_permission_detail.html:86
#: perms/templates/perms/remote_app_permission_detail.html:78
#: terminal/models.py:167 terminal/templates/terminal/session_list.html:34
#: xpack/plugins/change_auth_plan/models.py:249
#: xpack/plugins/change_auth_plan/models.py:419
#: xpack/plugins/change_auth_plan/models.py:250
#: xpack/plugins/change_auth_plan/models.py:426
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17
#: xpack/plugins/gathered_user/models.py:143
@ -2557,14 +2558,14 @@ msgid "Show"
msgstr "显示"
#: authentication/templates/authentication/_access_key_modal.html:66
#: users/models/user.py:335 users/templates/users/user_profile.html:94
#: users/models/user.py:352 users/templates/users/user_profile.html:94
#: users/templates/users/user_profile.html:163
#: users/templates/users/user_profile.html:166
msgid "Disable"
msgstr "禁用"
#: authentication/templates/authentication/_access_key_modal.html:67
#: users/models/user.py:336 users/templates/users/user_profile.html:92
#: users/models/user.py:353 users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:170
msgid "Enable"
msgstr "启用"
@ -2626,7 +2627,7 @@ msgstr "改变世界,从一点点开始。"
#: authentication/templates/authentication/login.html:45
#: authentication/templates/authentication/login.html:76
#: authentication/templates/authentication/xpack_login.html:110
#: authentication/templates/authentication/xpack_login.html:112
#: templates/_header_bar.html:83
msgid "Login"
msgstr "登录"
@ -2637,7 +2638,7 @@ msgid "Captcha invalid"
msgstr "验证码错误"
#: authentication/templates/authentication/login.html:87
#: authentication/templates/authentication/xpack_login.html:114
#: authentication/templates/authentication/xpack_login.html:116
#: users/templates/users/forgot_password.html:10
#: users/templates/users/forgot_password.html:25
msgid "Forgot password"
@ -2701,11 +2702,11 @@ msgstr "返回"
msgid "Welcome back, please enter username and password to login"
msgstr "欢迎回来,请输入用户名和密码登录"
#: authentication/views/login.py:71
#: authentication/views/login.py:70
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:170
#: authentication/views/login.py:157
msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page"
@ -2713,15 +2714,15 @@ msgstr ""
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
" 不要关闭本页面"
#: authentication/views/login.py:175
#: authentication/views/login.py:162
msgid "No ticket found"
msgstr "没有发现工单"
#: authentication/views/login.py:198
#: authentication/views/login.py:185
msgid "Logout success"
msgstr "退出登录成功"
#: authentication/views/login.py:199
#: authentication/views/login.py:186
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
@ -2905,8 +2906,8 @@ msgstr "完成时间"
#: ops/models/adhoc.py:358 ops/templates/ops/adhoc_history.html:57
#: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:17
#: xpack/plugins/change_auth_plan/models.py:252
#: xpack/plugins/change_auth_plan/models.py:422
#: xpack/plugins/change_auth_plan/models.py:253
#: xpack/plugins/change_auth_plan/models.py:429
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16
#: xpack/plugins/gathered_user/models.py:146
@ -3197,7 +3198,7 @@ msgstr "提示RDP 协议不支持单独控制上传或下载文件"
#: perms/templates/perms/asset_permission_list.html:118
#: perms/templates/perms/remote_app_permission_list.html:16
#: templates/_nav.html:21 users/forms.py:313 users/models/group.py:26
#: users/models/user.py:388 users/templates/users/_select_user_modal.html:16
#: users/models/user.py:416 users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:218
#: users/templates/users/user_list.html:38
#: xpack/plugins/orgs/templates/orgs/org_list.html:16
@ -3239,7 +3240,7 @@ msgstr "资产授权"
#: perms/models/base.py:53
#: perms/templates/perms/asset_permission_detail.html:90
#: perms/templates/perms/remote_app_permission_detail.html:82
#: users/models/user.py:420 users/templates/users/user_detail.html:108
#: users/models/user.py:448 users/templates/users/user_detail.html:108
#: users/templates/users/user_profile.html:120
msgid "Date expired"
msgstr "失效日期"
@ -3456,33 +3457,41 @@ msgstr "远程应用授权用户列表"
msgid "RemoteApp permission RemoteApp list"
msgstr "远程应用授权远程应用列表"
#: settings/api.py:31
#: settings/api.py:37
msgid "Test mail sent to {}, please check"
msgstr "邮件已经发送{}, 请检查"
#: settings/api.py:70
#: settings/api.py:76
msgid "Test ldap success"
msgstr "连接LDAP成功"
#: settings/api.py:107
msgid "LDAP attr map not valid"
msgstr ""
#: settings/api.py:116
msgid "Match {} s users"
msgstr "匹配 {} 个用户"
#: settings/api.py:166
msgid "succeed: {} failed: {} total: {}"
msgstr "成功:{} 失败:{} 总数:{}"
#: settings/api.py:224
msgid "Get ldap users is None"
msgstr "获取 LDAP 用户为 None"
#: settings/api.py:188 settings/api.py:224
#: settings/api.py:231
msgid "Imported {} users successfully"
msgstr "导入 {} 个用户成功"
#: settings/api.py:262 settings/api.py:298
msgid ""
"Error: Account invalid (Please make sure the information such as Access key "
"or Secret key is correct)"
msgstr "错误:账户无效 (请确保 Access key 或 Secret key 等信息正确)"
#: settings/api.py:194 settings/api.py:230
#: settings/api.py:268 settings/api.py:304
msgid "Create succeed"
msgstr "创建成功"
#: settings/api.py:212 settings/api.py:250
#: settings/api.py:286 settings/api.py:324
#: settings/templates/settings/terminal_setting.html:154
msgid "Delete succeed"
msgstr "删除成功"
@ -3802,23 +3811,32 @@ msgstr "LDAP 用户列表"
msgid "Please submit the LDAP configuration before import"
msgstr "请先提交LDAP配置再进行导入"
#: settings/templates/settings/_ldap_list_users_modal.html:32
#: users/models/user.py:384 users/templates/users/user_detail.html:72
#: settings/templates/settings/_ldap_list_users_modal.html:26
msgid "Refresh cache"
msgstr "刷新缓存"
#: settings/templates/settings/_ldap_list_users_modal.html:33
#: users/models/user.py:412 users/templates/users/user_detail.html:72
#: users/templates/users/user_profile.html:59
msgid "Email"
msgstr "邮件"
#: settings/templates/settings/_ldap_list_users_modal.html:33
#: settings/templates/settings/_ldap_list_users_modal.html:34
msgid "Existing"
msgstr "已存在"
#: settings/templates/settings/_ldap_list_users_modal.html:144
msgid ""
"User is not currently selected, please check the user you want to import"
msgstr "当前无勾选用户,请勾选你想要导入的用户"
#: settings/templates/settings/basic_setting.html:15
#: settings/templates/settings/email_content_setting.html:15
#: settings/templates/settings/email_setting.html:15
#: settings/templates/settings/ldap_setting.html:15
#: settings/templates/settings/security_setting.html:15
#: settings/templates/settings/terminal_setting.html:16
#: settings/templates/settings/terminal_setting.html:49 settings/views.py:20
#: settings/templates/settings/terminal_setting.html:49 settings/views.py:21
msgid "Basic setting"
msgstr "基本设置"
@ -3827,7 +3845,7 @@ msgstr "基本设置"
#: settings/templates/settings/email_setting.html:18
#: settings/templates/settings/ldap_setting.html:18
#: settings/templates/settings/security_setting.html:18
#: settings/templates/settings/terminal_setting.html:20 settings/views.py:47
#: settings/templates/settings/terminal_setting.html:20 settings/views.py:48
msgid "Email setting"
msgstr "邮件设置"
@ -3836,7 +3854,7 @@ msgstr "邮件设置"
#: settings/templates/settings/email_setting.html:21
#: settings/templates/settings/ldap_setting.html:21
#: settings/templates/settings/security_setting.html:21
#: settings/templates/settings/terminal_setting.html:23 settings/views.py:186
#: settings/templates/settings/terminal_setting.html:23 settings/views.py:188
msgid "Email content setting"
msgstr "邮件内容设置"
@ -3845,7 +3863,7 @@ msgstr "邮件内容设置"
#: settings/templates/settings/email_setting.html:24
#: settings/templates/settings/ldap_setting.html:24
#: settings/templates/settings/security_setting.html:24
#: settings/templates/settings/terminal_setting.html:27 settings/views.py:74
#: settings/templates/settings/terminal_setting.html:27 settings/views.py:75
msgid "LDAP setting"
msgstr "LDAP设置"
@ -3854,7 +3872,7 @@ msgstr "LDAP设置"
#: settings/templates/settings/email_setting.html:27
#: settings/templates/settings/ldap_setting.html:27
#: settings/templates/settings/security_setting.html:27
#: settings/templates/settings/terminal_setting.html:31 settings/views.py:104
#: settings/templates/settings/terminal_setting.html:31 settings/views.py:106
msgid "Terminal setting"
msgstr "终端设置"
@ -3864,7 +3882,7 @@ msgstr "终端设置"
#: settings/templates/settings/ldap_setting.html:30
#: settings/templates/settings/security_setting.html:30
#: settings/templates/settings/security_setting.html:45
#: settings/templates/settings/terminal_setting.html:34 settings/views.py:159
#: settings/templates/settings/terminal_setting.html:34 settings/views.py:161
msgid "Security setting"
msgstr "安全设置"
@ -3884,15 +3902,10 @@ msgstr "文档类型"
msgid "Create User setting"
msgstr "创建用户设置"
#: settings/templates/settings/ldap_setting.html:68
#: settings/templates/settings/ldap_setting.html:66
msgid "Bulk import"
msgstr "一键导入"
#: settings/templates/settings/ldap_setting.html:116
msgid ""
"User is not currently selected, please check the user you want to import"
msgstr "当前无勾选用户,请勾选你想要导入的用户"
#: settings/templates/settings/replay_storage_create.html:66
msgid "Bucket"
msgstr "桶名称"
@ -3997,30 +4010,26 @@ msgstr "删除失败"
msgid "Are you sure about deleting it?"
msgstr "您确定删除吗?"
#: settings/utils.py:98
#: settings/utils/ldap.py:128
msgid "Search no entry matched in ou {}"
msgstr "在ou:{}中没有匹配条目"
#: settings/utils.py:172
msgid "The user source is not LDAP"
msgstr "用户来源不是LDAP"
#: settings/views.py:19 settings/views.py:46 settings/views.py:73
#: settings/views.py:103 settings/views.py:131 settings/views.py:144
#: settings/views.py:158 settings/views.py:185 templates/_nav.html:180
#: settings/views.py:20 settings/views.py:47 settings/views.py:74
#: settings/views.py:105 settings/views.py:133 settings/views.py:146
#: settings/views.py:160 settings/views.py:187 templates/_nav.html:180
msgid "Settings"
msgstr "系统设置"
#: settings/views.py:30 settings/views.py:57 settings/views.py:84
#: settings/views.py:116 settings/views.py:169 settings/views.py:196
#: settings/views.py:31 settings/views.py:58 settings/views.py:85
#: settings/views.py:118 settings/views.py:171 settings/views.py:198
msgid "Update setting successfully"
msgstr "更新设置成功"
#: settings/views.py:132
#: settings/views.py:134
msgid "Create replay storage"
msgstr "创建录像存储"
#: settings/views.py:145
#: settings/views.py:147
msgid "Create command storage"
msgstr "创建命令存储"
@ -4570,7 +4579,7 @@ msgstr "接受"
#: tickets/models/login_confirm.py:16
#: tickets/templates/tickets/login_confirm_ticket_detail.html:10
#: tickets/templates/tickets/login_confirm_ticket_list.html:70
#: tickets/templates/tickets/login_confirm_ticket_list.html:107
#: tickets/templates/tickets/login_confirm_ticket_list.html:108
msgid "Reject"
msgstr "拒绝"
@ -4608,12 +4617,12 @@ msgid ""
msgstr "你可以使用ssh客户端工具连接终端"
#: tickets/models/base.py:16 tickets/models/base.py:52
#: tickets/templates/tickets/login_confirm_ticket_list.html:102
#: tickets/templates/tickets/login_confirm_ticket_list.html:103
msgid "Open"
msgstr "开启"
#: tickets/models/base.py:17
#: tickets/templates/tickets/login_confirm_ticket_list.html:103
#: tickets/templates/tickets/login_confirm_ticket_list.html:104
msgid "Closed"
msgstr "关闭"
@ -4658,7 +4667,7 @@ msgstr "{} {} 这个工单"
#: tickets/models/login_confirm.py:15
#: tickets/templates/tickets/login_confirm_ticket_detail.html:9
#: tickets/templates/tickets/login_confirm_ticket_list.html:69
#: tickets/templates/tickets/login_confirm_ticket_list.html:106
#: tickets/templates/tickets/login_confirm_ticket_list.html:107
msgid "Approve"
msgstr "同意"
@ -4771,7 +4780,7 @@ msgstr "登录复核工单详情"
msgid "Could not reset self otp, use profile reset instead"
msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
#: users/forms.py:47 users/models/user.py:392
#: users/forms.py:47 users/models/user.py:420
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:88
#: users/templates/users/user_list.html:37
@ -4779,7 +4788,7 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
msgid "Role"
msgstr "角色"
#: users/forms.py:51 users/models/user.py:427
#: users/forms.py:51 users/models/user.py:455
#: users/templates/users/user_detail.html:104
#: users/templates/users/user_list.html:39
#: users/templates/users/user_profile.html:102
@ -4823,7 +4832,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户"
msgid "Set password"
msgstr "设置密码"
#: users/forms.py:152 xpack/plugins/change_auth_plan/models.py:88
#: users/forms.py:152 xpack/plugins/change_auth_plan/models.py:89
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
@ -4897,44 +4906,44 @@ msgstr "选择用户"
msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
#: users/models/user.py:131 users/models/user.py:517
#: users/models/user.py:148 users/models/user.py:545
msgid "Administrator"
msgstr "管理员"
#: users/models/user.py:133
#: users/models/user.py:150
msgid "Application"
msgstr "应用程序"
#: users/models/user.py:134 xpack/plugins/orgs/forms.py:30
#: users/models/user.py:151 xpack/plugins/orgs/forms.py:30
#: xpack/plugins/orgs/templates/orgs/org_list.html:14
msgid "Auditor"
msgstr "审计员"
#: users/models/user.py:144
#: users/models/user.py:161
msgid "Org admin"
msgstr "组织管理员"
#: users/models/user.py:146
#: users/models/user.py:163
msgid "Org auditor"
msgstr "组织审计员"
#: users/models/user.py:337 users/templates/users/user_profile.html:90
#: users/models/user.py:354 users/templates/users/user_profile.html:90
msgid "Force enable"
msgstr "强制启用"
#: users/models/user.py:395
#: users/models/user.py:423
msgid "Avatar"
msgstr "头像"
#: users/models/user.py:398 users/templates/users/user_detail.html:83
#: users/models/user.py:426 users/templates/users/user_detail.html:83
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:431
#: users/models/user.py:459
msgid "Date password last updated"
msgstr "最后更新密码日期"
#: users/models/user.py:520
#: users/models/user.py:548
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
@ -5753,8 +5762,8 @@ msgstr ""
"具</a> <br>注意: 如果同时设置了定期执行和周期执行,优先使用定期执行"
#: xpack/plugins/change_auth_plan/meta.py:9
#: xpack/plugins/change_auth_plan/models.py:116
#: xpack/plugins/change_auth_plan/models.py:256
#: xpack/plugins/change_auth_plan/models.py:117
#: xpack/plugins/change_auth_plan/models.py:257
#: xpack/plugins/change_auth_plan/views.py:33
#: xpack/plugins/change_auth_plan/views.py:50
#: xpack/plugins/change_auth_plan/views.py:74
@ -5765,20 +5774,20 @@ msgstr ""
msgid "Change auth plan"
msgstr "改密计划"
#: xpack/plugins/change_auth_plan/models.py:57
#: xpack/plugins/change_auth_plan/models.py:58
msgid "Custom password"
msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models.py:58
#: xpack/plugins/change_auth_plan/models.py:59
msgid "All assets use the same random password"
msgstr "所有资产使用相同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:59
#: xpack/plugins/change_auth_plan/models.py:60
msgid "All assets use different random password"
msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:78
#: xpack/plugins/change_auth_plan/models.py:147
#: xpack/plugins/change_auth_plan/models.py:79
#: xpack/plugins/change_auth_plan/models.py:148
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100
#: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91
@ -5787,8 +5796,8 @@ msgstr "所有资产使用不同的随机密码"
msgid "Cycle perform"
msgstr "周期执行"
#: xpack/plugins/change_auth_plan/models.py:83
#: xpack/plugins/change_auth_plan/models.py:145
#: xpack/plugins/change_auth_plan/models.py:84
#: xpack/plugins/change_auth_plan/models.py:146
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92
#: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83
@ -5797,37 +5806,37 @@ msgstr "周期执行"
msgid "Regularly perform"
msgstr "定期执行"
#: xpack/plugins/change_auth_plan/models.py:92
#: xpack/plugins/change_auth_plan/models.py:93
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74
msgid "Password rules"
msgstr "密码规则"
#: xpack/plugins/change_auth_plan/models.py:212
#: xpack/plugins/change_auth_plan/models.py:213
msgid "* For security, do not change {} user's password"
msgstr "* 为了安全,禁止更改 {} 用户的密码"
#: xpack/plugins/change_auth_plan/models.py:216
#: xpack/plugins/change_auth_plan/models.py:217
msgid "Assets is empty, please add the asset"
msgstr "资产为空,请添加资产"
#: xpack/plugins/change_auth_plan/models.py:260
#: xpack/plugins/change_auth_plan/models.py:261
msgid "Change auth plan snapshot"
msgstr "改密计划快照"
#: xpack/plugins/change_auth_plan/models.py:275
#: xpack/plugins/change_auth_plan/models.py:426
#: xpack/plugins/change_auth_plan/models.py:276
#: xpack/plugins/change_auth_plan/models.py:433
msgid "Change auth plan execution"
msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models.py:435
#: xpack/plugins/change_auth_plan/models.py:442
msgid "Change auth plan execution subtask"
msgstr "改密计划执行子任务"
#: xpack/plugins/change_auth_plan/models.py:453
#: xpack/plugins/change_auth_plan/models.py:460
msgid "Authentication failed"
msgstr "认证失败"
#: xpack/plugins/change_auth_plan/models.py:455
#: xpack/plugins/change_auth_plan/models.py:462
msgid "Connection timeout"
msgstr "连接超时"
@ -6437,6 +6446,12 @@ msgstr "密码匣子"
msgid "vault create"
msgstr "创建"
#~ msgid "succeed: {} failed: {} total: {}"
#~ msgstr "成功:{} 失败:{} 总数:{}"
#~ msgid "The user source is not LDAP"
#~ msgstr "用户来源不是LDAP"
#~ msgid "selected"
#~ msgstr "所选"
@ -6610,25 +6625,6 @@ msgstr "创建"
#~ msgid "Sync User"
#~ msgstr "同步用户"
#~ msgid "Have user but attr mapping error"
#~ msgstr "有用户但attr映射错误"
#~ msgid ""
#~ "Import {} users successfully; import {} users failed, the database "
#~ "already exists with the same name"
#~ msgstr "导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户"
#~ msgid ""
#~ "Import {} users successfully; import {} users failed, the database "
#~ "already exists with the same name; import {}users failed, "
#~ "BecauseTypeError' object has no attribute 'keys'"
#~ msgstr ""
#~ "导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户; 导入 "
#~ "{} 这些用户失败,因为对象没有属性'keys'"
#~ msgid "Import {} users successfully"
#~ msgstr "导入 {} 个用户成功"
#~ msgid ""
#~ "Import {} users successfully;import {} users failed, BecauseTypeError' "
#~ "object has no attribute 'keys'"

View File

@ -11,13 +11,6 @@ from .utils.asset_permission import AssetPermissionUtilV2
logger = get_logger(__file__)
permission_m2m_senders = (
AssetPermission.nodes.through,
AssetPermission.assets.through,
AssetPermission.users.through,
AssetPermission.user_groups.through,
)
@receiver([post_save, post_delete], sender=AssetPermission)
@on_transaction_commit

View File

@ -12,21 +12,27 @@ from django.conf import settings
from django.core.mail import send_mail
from django.utils.translation import ugettext_lazy as _
from .models import Setting
from .utils import (
LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil,
LDAP_USE_CACHE_FLAGS
)
from .tasks import sync_ldap_user_task
from common.permissions import IsOrgAdmin, IsSuperUser
from common.utils import get_logger
from .models import Setting
from .utils import LDAPUtil
from .serializers import (
MailTestSerializer, LDAPTestSerializer, LDAPUserSerializer,
PublicSettingSerializer,
)
from users.models import User
logger = get_logger(__file__)
class MailTestingAPI(APIView):
permission_classes = (IsOrgAdmin,)
permission_classes = (IsSuperUser,)
serializer_class = MailTestSerializer
success_message = _("Test mail sent to {}, please check")
@ -65,70 +71,83 @@ class MailTestingAPI(APIView):
class LDAPTestingAPI(APIView):
permission_classes = (IsOrgAdmin,)
permission_classes = (IsSuperUser,)
serializer_class = LDAPTestSerializer
success_message = _("Test ldap success")
@staticmethod
def get_ldap_util(serializer):
host = serializer.validated_data["AUTH_LDAP_SERVER_URI"]
def get_ldap_config(serializer):
server_uri = serializer.validated_data["AUTH_LDAP_SERVER_URI"]
bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"]
password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"]
use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False)
search_ougroup = serializer.validated_data["AUTH_LDAP_SEARCH_OU"]
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
try:
attr_map = json.loads(attr_map)
except json.JSONDecodeError:
return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401)
util = LDAPUtil(
use_settings_config=False, server_uri=host, bind_dn=bind_dn,
password=password, use_ssl=use_ssl,
search_ougroup=search_ougroup, search_filter=search_filter,
attr_map=attr_map
)
return util
config = {
'server_uri': server_uri,
'bind_dn': bind_dn,
'password': password,
'use_ssl': use_ssl,
'search_ougroup': search_ougroup,
'search_filter': search_filter,
'attr_map': json.loads(attr_map),
}
return config
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return Response({"error": str(serializer.errors)}, status=401)
util = self.get_ldap_util(serializer)
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
try:
users = util.search_user_items()
json.loads(attr_map)
except json.JSONDecodeError:
return Response({"error": _("LDAP attr map not valid")}, status=401)
config = self.get_ldap_config(serializer)
util = LDAPServerUtil(config=config)
try:
users = util.search()
except Exception as e:
return Response({"error": str(e)}, status=401)
if len(users) > 0:
return Response({"msg": _("Match {} s users").format(len(users))})
else:
return Response({"error": "Have user but attr mapping error"}, status=401)
return Response({"msg": _("Match {} s users").format(len(users))})
class LDAPUserListApi(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
permission_classes = (IsSuperUser,)
serializer_class = LDAPUserSerializer
def get_queryset_from_cache(self):
search_value = self.request.query_params.get('search')
users = LDAPCacheUtil().search(search_value=search_value)
return users
def get_queryset_from_server(self):
search_value = self.request.query_params.get('search')
users = LDAPServerUtil().search(search_value=search_value)
return users
def get_queryset(self):
if hasattr(self, 'swagger_fake_view'):
return []
q = self.request.query_params.get('search')
try:
util = LDAPUtil()
extra_filter = util.construct_extra_filter(util.SEARCH_FIELD_ALL, q)
users = util.search_user_items(extra_filter)
except Exception as e:
users = []
logger.error(e)
# 前端data_table会根据row.id对table.selected值进行操作
for user in users:
user['id'] = user['username']
cache_police = self.request.query_params.get('cache_police', True)
if cache_police in LDAP_USE_CACHE_FLAGS:
users = self.get_queryset_from_cache()
else:
users = self.get_queryset_from_server()
return users
@staticmethod
def processing_queryset(queryset):
db_username_list = User.objects.all().values_list('username', flat=True)
for q in queryset:
q['id'] = q['username']
q['existing'] = q['username'] in db_username_list
return queryset
def sort_queryset(self, queryset):
order_by = self.request.query_params.get('order')
if not order_by:
@ -141,32 +160,87 @@ class LDAPUserListApi(generics.ListAPIView):
queryset = sorted(queryset, key=lambda x: x[order_by], reverse=reverse)
return queryset
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
def filter_queryset(self, queryset):
if queryset is None:
return queryset
queryset = self.processing_queryset(queryset)
queryset = self.sort_queryset(queryset)
page = self.paginate_queryset(queryset)
if page is not None:
return self.get_paginated_response(page)
return Response(queryset)
return queryset
def list(self, request, *args, **kwargs):
cache_police = self.request.query_params.get('cache_police', True)
# 不是用缓存
if cache_police not in LDAP_USE_CACHE_FLAGS:
return super().list(request, *args, **kwargs)
try:
queryset = self.get_queryset()
except Exception as e:
data = {'error': str(e)}
return Response(data=data, status=400)
# 缓存有数据
if queryset is not None:
return super().list(request, *args, **kwargs)
sync_util = LDAPSyncUtil()
# 还没有同步任务
if sync_util.task_no_start:
task = sync_ldap_user_task.delay()
data = {'msg': 'Cache no data, sync task {} started.'.format(task.id)}
return Response(data=data, status=409)
# 同步任务正在执行
if sync_util.task_is_running:
data = {'msg': 'synchronization is running.'}
return Response(data=data, status=409)
# 同步任务执行结束
if sync_util.task_is_over:
msg = sync_util.get_task_error_msg()
data = {'error': 'Synchronization task report error: {}'.format(msg)}
return Response(data=data, status=400)
return super().list(request, *args, **kwargs)
class LDAPUserSyncAPI(APIView):
permission_classes = (IsOrgAdmin,)
class LDAPUserImportAPI(APIView):
permission_classes = (IsSuperUser,)
def get_ldap_users(self):
username_list = self.request.data.get('username_list', [])
cache_police = self.request.query_params.get('cache_police', True)
if cache_police in LDAP_USE_CACHE_FLAGS:
users = LDAPCacheUtil().search(search_users=username_list)
else:
users = LDAPServerUtil().search(search_users=username_list)
return users
def post(self, request):
username_list = request.data.get('username_list', [])
util = LDAPUtil()
try:
result = util.sync_users(username_list)
users = self.get_ldap_users()
except Exception as e:
logger.error(e, exc_info=True)
return Response({'error': str(e)}, status=401)
else:
msg = _("succeed: {} failed: {} total: {}").format(
result['succeed'], result['failed'], result['total']
)
return Response({'msg': msg})
if users is None:
return Response({'msg': _('Get ldap users is None')}, status=401)
errors = LDAPImportUtil().perform_import(users)
if errors:
return Response({'errors': errors}, status=401)
count = users if users is None else len(users)
return Response({'msg': _('Imported {} users successfully').format(count)})
class LDAPCacheRefreshAPI(generics.RetrieveAPIView):
permission_classes = (IsSuperUser,)
def retrieve(self, request, *args, **kwargs):
try:
LDAPSyncUtil().clear_cache()
except Exception as e:
logger.error(str(e))
return Response(data={'msg': str(e)}, status=400)
return Response(data={'msg': 'success'})
class ReplayStorageCreateAPI(APIView):

View File

@ -25,6 +25,7 @@ class LDAPTestSerializer(serializers.Serializer):
class LDAPUserSerializer(serializers.Serializer):
id = serializers.CharField()
username = serializers.CharField()
name = serializers.CharField()
email = serializers.CharField()
existing = serializers.BooleanField(read_only=True)

View File

@ -0,0 +1,4 @@
# coding: utf-8
#
from .ldap import *

View File

@ -0,0 +1,17 @@
# coding: utf-8
#
from celery import shared_task
from common.utils import get_logger
from ..utils import LDAPSyncUtil
__all__ = ['sync_ldap_user_task']
logger = get_logger(__file__)
@shared_task
def sync_ldap_user_task():
LDAPSyncUtil().perform_sync()

View File

@ -23,6 +23,7 @@
<div class="row">
<div class="col-lg-12 animated fadeInRight" id="split-right">
<div class="mail-box-header">
<div class="uc pull-left m-r-5"><a id="id_refresh_cache" class="btn btn-sm btn-primary"> {% trans "Refresh cache" %} </a></div>
<table class="table table-striped table-bordered table-hover " id="ldap_list_users_table" style="width: 100%">
<thead>
<tr>
@ -36,6 +37,9 @@
<tbody>
</tbody>
</table>
<div id="fake_datatable_wrapper_loading" class="dataTables_wrapper" style="display: block;">
<div id="ldap_list_users_table_processing" class="dataTables_processing panel panel-default">{% trans 'Loading' %}...</div>
</div>
</div>
</div>
</div>
@ -43,8 +47,11 @@
<script>
var ldap_users_table = 0;
var interval;
function initLdapUsersTable() {
if(ldap_users_table){
ldap_users_table.ajax.reload(null, false);
return ldap_users_table
}
var options = {
@ -68,21 +75,93 @@ function initLdapUsersTable() {
],
pageLength: 15
};
ldap_users_table = jumpserver.initServerSideDataTable(options);
return ldap_users_table
}
function testRequestLdapUser(){
$("#fake_datatable_wrapper_loading").css('display', 'block');
var the_url = "{% url 'api-settings:ldap-user-list' %}";
var error = function (data, status) {
if (status === 409){
console.log(data);
return
}
if (status === 400){
toastr.error(data);
$("#fake_datatable_wrapper_loading").css('display', 'none');
clearInterval(interval);
interval = undefined;
return
}
console.log(data, status)
};
var success = function() {
$("#fake_datatable_wrapper_loading").css('display', 'none');
initLdapUsersTable();
clearInterval(interval);
interval = undefined
};
requestApi({
url: the_url,
method: 'GET',
flash_message: false,
error: error,
success: success
});
}
function timingTestRequestLdapUser(){
if (interval !== undefined){
return
}
interval = setInterval(testRequestLdapUser, 2000);
}
$(document).ready(function(){
}).on('show.bs.modal', function () {
initLdapUsersTable();
timingTestRequestLdapUser()
})
.on('click','.close_btn1',function () {
window.location.reload()
.on('click', '#id_refresh_cache', function () {
var the_url = "{% url "api-settings:ldap-cache-refresh" %}";
function error(data) {
toastr.error(data)
}
function success(){
timingTestRequestLdapUser();
}
requestApi({
url: the_url,
method: 'GET',
error: error,
success: success
})
})
.on('click','.close_btn2',function () {
window.location.reload()
.on("click","#btn_ldap_modal_confirm",function () {
var username_list = ldap_users_table.selected;
if (username_list.length === 0){
var msg = "{% trans 'User is not currently selected, please check the user you want to import'%}";
toastr.error(msg);
return
}
var the_url = "{% url "api-settings:ldap-user-import" %}";
function error(message) {
toastr.error(message)
}
function success(message) {
toastr.success(message.msg);
ldap_users_table.selected = [];
timingTestRequestLdapUser();
}
requestApi({
url: the_url,
body: JSON.stringify({'username_list':username_list}),
method: "POST",
flash_message: false,
success: success,
error: error
});
})
</script>

View File

@ -63,9 +63,8 @@
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button class="btn btn-default btn-test" type="button"> {% trans 'Test connection' %}</button>
{# <button class="btn btn-primary sync_button " data-toggle="modal" data-target="#sync_users_modal" type="button">{% trans 'Synchronization' %}</button>#}
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
<button class="btn btn-default sync_button " data-toggle="modal" data-target="#ldap_list_users_modal" type="button">{% trans 'Bulk import' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
@ -109,33 +108,6 @@ $(document).ready(function () {
error: error
});
})
.on("click","#btn_ldap_modal_confirm",function () {
var username_list = ldap_users_table.selected;
if (username_list.length === 0){
var msg = "{% trans 'User is not currently selected, please check the user you want to import'%}";
toastr.error(msg);
return
}
var the_url = "{% url "api-settings:ldap-user-sync" %}";
function error(message) {
toastr.error(message)
}
function success(message) {
toastr.success(message.msg)
}
requestApi({
url: the_url,
body: JSON.stringify({'username_list':username_list}),
method: "POST",
flash_message: false,
success: success,
error: error
});
})
</script>
{% endblock %}

View File

@ -10,7 +10,8 @@ urlpatterns = [
path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'),
path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
path('ldap/users/', api.LDAPUserListApi.as_view(), name='ldap-user-list'),
path('ldap/users/sync/', api.LDAPUserSyncAPI.as_view(), name='ldap-user-sync'),
path('ldap/users/import/', api.LDAPUserImportAPI.as_view(), name='ldap-user-import'),
path('ldap/cache/refresh/', api.LDAPCacheRefreshAPI.as_view(), name='ldap-cache-refresh'),
path('terminal/replay-storage/create/', api.ReplayStorageCreateAPI.as_view(), name='replay-storage-create'),
path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'),
path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'),

View File

@ -1,219 +0,0 @@
# -*- coding: utf-8 -*-
#
from ldap3 import Server, Connection
from django.utils.translation import ugettext_lazy as _
from users.models import User
from users.utils import construct_user_email
from common.utils import get_logger
from common.const import LDAP_AD_ACCOUNT_DISABLE
from .models import settings
logger = get_logger(__file__)
class LDAPOUGroupException(Exception):
pass
class LDAPUtil:
_conn = None
SEARCH_FIELD_ALL = 'all'
SEARCH_FIELD_USERNAME = 'username'
def __init__(self, use_settings_config=True, server_uri=None, bind_dn=None,
password=None, use_ssl=None, search_ougroup=None,
search_filter=None, attr_map=None, auth_ldap=None):
# config
self.paged_size = settings.AUTH_LDAP_SEARCH_PAGED_SIZE
if use_settings_config:
self._load_config_from_settings()
else:
self.server_uri = server_uri
self.bind_dn = bind_dn
self.password = password
self.use_ssl = use_ssl
self.search_ougroup = search_ougroup
self.search_filter = search_filter
self.attr_map = attr_map
self.auth_ldap = auth_ldap
def _load_config_from_settings(self):
self.server_uri = settings.AUTH_LDAP_SERVER_URI
self.bind_dn = settings.AUTH_LDAP_BIND_DN
self.password = settings.AUTH_LDAP_BIND_PASSWORD
self.use_ssl = settings.AUTH_LDAP_START_TLS
self.search_ougroup = settings.AUTH_LDAP_SEARCH_OU
self.search_filter = settings.AUTH_LDAP_SEARCH_FILTER
self.attr_map = settings.AUTH_LDAP_USER_ATTR_MAP
self.auth_ldap = settings.AUTH_LDAP
@property
def connection(self):
if self._conn is None:
server = Server(self.server_uri, use_ssl=self.use_ssl)
conn = Connection(server, self.bind_dn, self.password)
conn.bind()
self._conn = conn
return self._conn
@staticmethod
def get_user_by_username(username):
try:
user = User.objects.get(username=username)
except Exception as e:
return None
else:
return user
def _ldap_entry_to_user_item(self, entry):
user_item = {}
for attr, mapping in self.attr_map.items():
if not hasattr(entry, mapping):
continue
value = getattr(entry, mapping).value or ''
if mapping.lower() == 'useraccountcontrol' and attr == 'is_active'\
and value:
value = int(value) & LDAP_AD_ACCOUNT_DISABLE \
!= LDAP_AD_ACCOUNT_DISABLE
user_item[attr] = value
return user_item
def _search_user_items_ou(self, search_ou, extra_filter=None, cookie=None):
search_filter = self.search_filter % {"user": "*"}
if extra_filter:
search_filter = '(&{}{})'.format(search_filter, extra_filter)
ok = self.connection.search(
search_ou, search_filter,
attributes=list(self.attr_map.values()),
paged_size=self.paged_size, paged_cookie=cookie
)
if not ok:
error = _("Search no entry matched in ou {}".format(search_ou))
raise LDAPOUGroupException(error)
user_items = []
for entry in self.connection.entries:
user_item = self._ldap_entry_to_user_item(entry)
user = self.get_user_by_username(user_item['username'])
user_item['existing'] = bool(user)
if user_item in user_items:
continue
user_items.append(user_item)
return user_items
def _cookie(self):
if self.paged_size is None:
cookie = None
else:
cookie = self.connection.result['controls']['1.2.840.113556.1.4.319']['value']['cookie']
return cookie
def search_user_items(self, extra_filter=None):
user_items = []
logger.info("Search user items")
for search_ou in str(self.search_ougroup).split("|"):
logger.info("Search user search ou: {}".format(search_ou))
_user_items = self._search_user_items_ou(search_ou, extra_filter=extra_filter)
user_items.extend(_user_items)
while self._cookie():
logger.info("Page Search user search ou: {}".format(search_ou))
_user_items = self._search_user_items_ou(search_ou, extra_filter, self._cookie())
user_items.extend(_user_items)
logger.info("Search user items end")
return user_items
def construct_extra_filter(self, field, q):
if not q:
return None
extra_filter = ''
if field == self.SEARCH_FIELD_ALL:
for attr in self.attr_map.values():
extra_filter += '({}={})'.format(attr, q)
extra_filter = '(|{})'.format(extra_filter)
return extra_filter
if field == self.SEARCH_FIELD_USERNAME and isinstance(q, list):
attr = self.attr_map.get('username')
for username in q:
extra_filter += '({}={})'.format(attr, username)
extra_filter = '(|{})'.format(extra_filter)
return extra_filter
def search_filter_user_items(self, username_list):
extra_filter = self.construct_extra_filter(
self.SEARCH_FIELD_USERNAME, username_list
)
user_items = self.search_user_items(extra_filter)
return user_items
@staticmethod
def save_user(user, user_item):
for field, value in user_item.items():
if not hasattr(user, field):
continue
if isinstance(getattr(user, field), bool):
if isinstance(value, str):
value = value.lower()
value = value in ['true', 1, True]
setattr(user, field, value)
user.save()
def update_user(self, user_item):
user = self.get_user_by_username(user_item['username'])
if user.source != User.SOURCE_LDAP:
msg = _('The user source is not LDAP')
return False, msg
try:
self.save_user(user, user_item)
except Exception as e:
logger.error(e, exc_info=True)
return False, str(e)
else:
return True, None
def create_user(self, user_item):
user = User(source=User.SOURCE_LDAP)
try:
self.save_user(user, user_item)
except Exception as e:
logger.error(e, exc_info=True)
return False, str(e)
else:
return True, None
@staticmethod
def construct_user_email(user_item):
username = user_item['username']
email = user_item.get('email', '')
email = construct_user_email(username, email)
return email
def create_or_update_users(self, user_items):
succeed = failed = 0
for user_item in user_items:
exist = user_item.pop('existing', False)
user_item['email'] = self.construct_user_email(user_item)
if not exist:
ok, error = self.create_user(user_item)
else:
ok, error = self.update_user(user_item)
if not ok:
logger.info("Failed User: {}".format(user_item))
failed += 1
else:
succeed += 1
result = {'total': len(user_items), 'succeed': succeed, 'failed': failed}
return result
def sync_users(self, username_list=None):
user_items = self.search_filter_user_items(username_list)
result = self.create_or_update_users(user_items)
return result

View File

@ -0,0 +1,4 @@
# coding: utf-8
#
from .ldap import *

338
apps/settings/utils/ldap.py Normal file
View File

@ -0,0 +1,338 @@
# coding: utf-8
#
from ldap3 import Server, Connection
from django.conf import settings
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from common.const import LDAP_AD_ACCOUNT_DISABLE
from common.utils import timeit, get_logger
from users.utils import construct_user_email
from users.models import User
logger = get_logger(__file__)
__all__ = [
'LDAPConfig', 'LDAPServerUtil', 'LDAPCacheUtil', 'LDAPImportUtil',
'LDAPSyncUtil', 'LDAP_USE_CACHE_FLAGS'
]
LDAP_USE_CACHE_FLAGS = [1, '1', 'true', 'True', True]
class LDAPOUGroupException(Exception):
pass
class LDAPConfig(object):
def __init__(self, config=None):
self.server_uri = None
self.bind_dn = None
self.password = None
self.use_ssl = None
self.search_ougroup = None
self.search_filter = None
self.attr_map = None
if isinstance(config, dict):
self.load_from_config(config)
else:
self.load_from_settings()
def load_from_config(self, config):
self.server_uri = config.get('server_uri')
self.bind_dn = config.get('bind_dn')
self.password = config.get('password')
self.use_ssl = config.get('use_ssl')
self.search_ougroup = config.get('search_ougroup')
self.search_filter = config.get('search_filter')
self.attr_map = config.get('attr_map')
def load_from_settings(self):
self.server_uri = settings.AUTH_LDAP_SERVER_URI
self.bind_dn = settings.AUTH_LDAP_BIND_DN
self.password = settings.AUTH_LDAP_BIND_PASSWORD
self.use_ssl = settings.AUTH_LDAP_START_TLS
self.search_ougroup = settings.AUTH_LDAP_SEARCH_OU
self.search_filter = settings.AUTH_LDAP_SEARCH_FILTER
self.attr_map = settings.AUTH_LDAP_USER_ATTR_MAP
class LDAPServerUtil(object):
def __init__(self, config=None):
if isinstance(config, dict):
self.config = LDAPConfig(config=config)
elif isinstance(config, LDAPConfig):
self.config = config
else:
self.config = LDAPConfig()
self._conn = None
self._paged_size = self.get_paged_size()
self.search_users = None
self.search_value = None
@property
def connection(self):
if self._conn:
return self._conn
server = Server(self.config.server_uri, use_ssl=self.config.use_ssl)
conn = Connection(server, self.config.bind_dn, self.config.password)
conn.bind()
self._conn = conn
return self._conn
@staticmethod
def get_paged_size():
paged_size = settings.AUTH_LDAP_SEARCH_PAGED_SIZE
if isinstance(paged_size, int):
return paged_size
return None
def paged_cookie(self):
if self._paged_size is None:
return None
cookie = self.connection.result['controls']['1.2.840.113556.1.4.319']['value']['cookie']
return cookie
def get_search_filter_extra(self):
extra = ''
if self.search_users:
mapping_username = self.config.attr_map.get('username')
for user in self.search_users:
extra += '({}={})'.format(mapping_username, user)
return '(|{})'.format(extra)
if self.search_value:
for attr in self.config.attr_map.values():
extra += '({}={})'.format(attr, self.search_value)
return '(|{})'.format(extra)
return extra
def get_search_filter(self):
search_filter = self.config.search_filter % {'user': '*'}
search_filter_extra = self.get_search_filter_extra()
if search_filter_extra:
search_filter = '(&{}{})'.format(search_filter, search_filter_extra)
return search_filter
def search_user_entries_ou(self, search_ou, paged_cookie=None):
search_filter = self.get_search_filter()
attributes = list(self.config.attr_map.values())
ok = self.connection.search(
search_base=search_ou, search_filter=search_filter,
attributes=attributes, paged_size=self._paged_size,
paged_cookie=paged_cookie
)
if not ok:
error = _("Search no entry matched in ou {}".format(search_ou))
raise LDAPOUGroupException(error)
@timeit
def search_user_entries(self):
logger.info("Search user entries")
user_entries = list()
search_ous = str(self.config.search_ougroup).split('|')
for search_ou in search_ous:
logger.info("Search user entries ou: {}".format(search_ou))
self.search_user_entries_ou(search_ou)
user_entries.extend(self.connection.entries)
while self.paged_cookie():
self.search_user_entries_ou(search_ou, self.paged_cookie())
user_entries.extend(self.connection.entries)
return user_entries
def user_entry_to_dict(self, entry):
user = {}
attr_map = self.config.attr_map.items()
for attr, mapping in attr_map:
if not hasattr(entry, mapping):
continue
value = getattr(entry, mapping).value or ''
if attr == 'is_active' and mapping.lower() == 'useraccountcontrol' \
and value:
value = int(value) & LDAP_AD_ACCOUNT_DISABLE != LDAP_AD_ACCOUNT_DISABLE
user[attr] = value
return user
@timeit
def user_entries_to_dict(self, user_entries):
users = []
for user_entry in user_entries:
user = self.user_entry_to_dict(user_entry)
users.append(user)
return users
@timeit
def search(self, search_users=None, search_value=None):
logger.info("Search ldap users")
self.search_users = search_users
self.search_value = search_value
user_entries = self.search_user_entries()
users = self.user_entries_to_dict(user_entries)
return users
class LDAPCacheUtil(object):
CACHE_KEY_USERS = 'CACHE_KEY_LDAP_USERS'
def __init__(self):
self.search_users = None
self.search_value = None
def set_users(self, users):
logger.info('Set ldap users to cache, count: {}'.format(len(users)))
cache.set(self.CACHE_KEY_USERS, users, None)
def get_users(self):
users = cache.get(self.CACHE_KEY_USERS)
count = users if users is None else len(users)
logger.info('Get ldap users from cache, count: {}'.format(count))
return users
def delete_users(self):
logger.info('Delete ldap users from cache')
cache.delete(self.CACHE_KEY_USERS)
def filter_users(self, users):
if users is None:
return users
if self.search_users:
filter_users = [
user for user in users
if user['username'] in self.search_users
]
elif self.search_value:
filter_users = [
user for user in users
if self.search_value in ','.join(user.values())
]
else:
filter_users = users
return filter_users
def search(self, search_users=None, search_value=None):
self.search_users = search_users
self.search_value = search_value
users = self.get_users()
users = self.filter_users(users)
return users
class LDAPSyncUtil(object):
CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG = 'CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG'
CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS = 'CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS'
TASK_STATUS_IS_RUNNING = 'RUNNING'
TASK_STATUS_IS_OVER = 'OVER'
def __init__(self):
self.server_util = LDAPServerUtil()
self.cache_util = LDAPCacheUtil()
self.task_error_msg = None
def clear_cache(self):
logger.info('Clear ldap sync cache')
self.delete_task_status()
self.delete_task_error_msg()
self.cache_util.delete_users()
@property
def task_no_start(self):
status = self.get_task_status()
return status is None
@property
def task_is_running(self):
status = self.get_task_status()
return status == self.TASK_STATUS_IS_RUNNING
@property
def task_is_over(self):
status = self.get_task_status()
return status == self.TASK_STATUS_IS_OVER
def set_task_status(self, status):
logger.info('Set task status: {}'.format(status))
cache.set(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS, status, None)
def get_task_status(self):
status = cache.get(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS)
logger.info('Get task status: {}'.format(status))
return status
def delete_task_status(self):
logger.info('Delete task status')
cache.delete(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS)
def set_task_error_msg(self, error_msg):
logger.info('Set task error msg')
cache.set(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG, error_msg, None)
def get_task_error_msg(self):
logger.info('Get task error msg')
error_msg = cache.get(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG)
return error_msg
def delete_task_error_msg(self):
logger.info('Delete task error msg')
cache.delete(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG)
def pre_sync(self):
self.set_task_status(self.TASK_STATUS_IS_RUNNING)
def sync(self):
users = self.server_util.search()
self.cache_util.set_users(users)
def post_sync(self):
self.set_task_status(self.TASK_STATUS_IS_OVER)
def perform_sync(self):
logger.info('Start perform sync ldap users from server to cache')
self.pre_sync()
try:
self.sync()
except Exception as e:
error_msg = str(e)
logger.error(error_msg)
self.set_task_error_msg(error_msg)
self.post_sync()
logger.info('End perform sync ldap users from server to cache')
class LDAPImportUtil(object):
def __init__(self):
pass
@staticmethod
def get_user_email(user):
username = user['username']
email = user['email']
email = construct_user_email(username, email)
return email
def update_or_create(self, user):
user['email'] = self.get_user_email(user)
if user['username'] not in ['admin']:
user['source'] = User.SOURCE_LDAP
obj, created = User.objects.update_or_create(
username=user['username'], defaults=user
)
return obj, created
def perform_import(self, users):
logger.info('Start perform import ldap users, count: {}'.format(len(users)))
errors = []
for user in users:
try:
self.update_or_create(user)
except Exception as e:
errors.append({user['username']: str(e)})
logger.error(e)
logger.info('End perform import ldap users')
return errors

View File

@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _
from common.permissions import PermissionsMixin, IsSuperUser
from common import utils
from .utils import LDAPSyncUtil
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
TerminalSettingForm, SecuritySettingForm, EmailContentSettingForm
@ -83,6 +84,7 @@ class LDAPSettingView(PermissionsMixin, TemplateView):
form.save()
msg = _("Update setting successfully")
messages.success(request, msg)
LDAPSyncUtil().clear_cache()
return redirect('settings:ldap-setting')
else:
context = self.get_context_data()

View File

@ -2,11 +2,11 @@
#
from django.dispatch import receiver
# from django.db.models.signals import post_save
from django.db.models.signals import post_save, m2m_changed
from common.utils import get_logger
from .signals import post_user_create
# from .models import User
from .models import User
logger = get_logger(__file__)
@ -28,3 +28,14 @@ def on_user_create(sender, user=None, **kwargs):
logger.info(" - Sending welcome mail ...".format(user.name))
if user.email:
send_user_created_mail(user)
@receiver(m2m_changed, sender=User.groups.through)
def on_user_groups_change(sender, instance=None, action='', **kwargs):
"""
资产节点发生变化时刷新节点
"""
if action.startswith('post'):
logger.debug("User group member change signal recv: {}".format(instance))
from perms.utils import AssetPermissionUtilV2
AssetPermissionUtilV2.expire_all_user_tree_cache()

View File

@ -1,17 +1,20 @@
# -*- coding: utf-8 -*-
#
import sys
from celery import shared_task
from django.conf import settings
from ops.celery.utils import create_or_update_celery_periodic_tasks
from ops.celery.utils import (
create_or_update_celery_periodic_tasks, disable_celery_periodic_task
)
from ops.celery.decorator import after_app_ready_start
from common.utils import get_logger
from .models import User
from .utils import (
send_password_expiration_reminder_mail, send_user_expiration_reminder_mail
)
from settings.utils import LDAPUtil
from settings.utils import LDAPServerUtil, LDAPImportUtil
logger = get_logger(__file__)
@ -70,19 +73,26 @@ def check_user_expired_periodic():
@shared_task
def sync_ldap_user():
logger.info("Start sync ldap user periodic task")
util = LDAPUtil()
result = util.sync_users()
logger.info("Result: {}".format(result))
def import_ldap_user():
logger.info("Start import ldap user task")
util_server = LDAPServerUtil()
util_import = LDAPImportUtil()
users = util_server.search()
errors = util_import.perform_import(users)
if errors:
logger.error("Imported LDAP users errors: {}".format(errors))
else:
logger.info('Imported {} users successfully'.format(len(users)))
@shared_task
@after_app_ready_start
def sync_ldap_user_periodic():
def import_ldap_user_periodic():
if not settings.AUTH_LDAP:
return
if not settings.AUTH_LDAP_SYNC_IS_PERIODIC:
task_name = sys._getframe().f_code.co_name
disable_celery_periodic_task(task_name)
return
interval = settings.AUTH_LDAP_SYNC_INTERVAL
@ -91,10 +101,9 @@ def sync_ldap_user_periodic():
else:
interval = None
crontab = settings.AUTH_LDAP_SYNC_CRONTAB
tasks = {
'sync_ldap_user_periodic': {
'task': sync_ldap_user.name,
'import_ldap_user_periodic': {
'task': import_ldap_user.name,
'interval': interval,
'crontab': crontab,
'enabled': True,