mirror of https://github.com/jumpserver/jumpserver
[Update] Merge branch dev to 1.5.5
commit
5434b65773
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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.
|
@ -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, "
|
||||
#~ "Because’TypeError' object has no attribute 'keys'"
|
||||
#~ msgstr ""
|
||||
#~ "导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户; 导入 "
|
||||
#~ "{} 这些用户失败,因为对象没有属性'keys'"
|
||||
|
||||
#~ msgid "Import {} users successfully"
|
||||
#~ msgstr "导入 {} 个用户成功"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Import {} users successfully;import {} users failed, Because’TypeError' "
|
||||
#~ "object has no attribute 'keys'"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
|
||||
from .ldap import *
|
|
@ -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()
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
|
||||
from .ldap import *
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue