mirror of https://github.com/jumpserver/jumpserver
perf(orgs): 默认组织改为实体组织,并支持全局组织 (#5617)
* perf(orgs): 默认组织改为实体组织 * perf: 添加获取当前组织信息的api * perf: 资产列表在 root 组织下的表现 * fix: 修复 root 组织引起的问题 * perf: 优化OrgModelMixin save; org_root获取; org_roles获取; UserCanUseCurrentOrg权限类 Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: Bai <bugatti_it@163.com>pull/5669/head
parent
51c9a89b1f
commit
a56ac7b34e
|
@ -127,9 +127,13 @@ class NodeChildrenApi(generics.ListCreateAPIView):
|
|||
def get_object(self):
|
||||
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
|
||||
key = self.request.query_params.get("key")
|
||||
|
||||
if not pk and not key:
|
||||
node = Node.org_root()
|
||||
self.is_initial = True
|
||||
if current_org.is_root():
|
||||
node = None
|
||||
else:
|
||||
node = Node.org_root()
|
||||
return node
|
||||
if pk:
|
||||
node = get_object_or_404(Node, pk=pk)
|
||||
|
@ -137,16 +141,26 @@ class NodeChildrenApi(generics.ListCreateAPIView):
|
|||
node = get_object_or_404(Node, key=key)
|
||||
return node
|
||||
|
||||
def get_org_root_queryset(self, query_all):
|
||||
if query_all:
|
||||
return Node.objects.all()
|
||||
else:
|
||||
return Node.org_root_nodes()
|
||||
|
||||
def get_queryset(self):
|
||||
query_all = self.request.query_params.get("all", "0") == "all"
|
||||
if not self.instance:
|
||||
return Node.objects.none()
|
||||
|
||||
if self.is_initial and current_org.is_root():
|
||||
return self.get_org_root_queryset(query_all)
|
||||
|
||||
if self.is_initial:
|
||||
with_self = True
|
||||
else:
|
||||
with_self = False
|
||||
|
||||
if not self.instance:
|
||||
return Node.objects.none()
|
||||
|
||||
if query_all:
|
||||
queryset = self.instance.get_all_children(with_self=with_self)
|
||||
else:
|
||||
|
@ -178,7 +192,7 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
|
|||
|
||||
def get_assets(self):
|
||||
include_assets = self.request.query_params.get('assets', '0') == '1'
|
||||
if not include_assets:
|
||||
if not self.instance or not include_assets:
|
||||
return []
|
||||
assets = self.instance.get_assets().only(
|
||||
"id", "hostname", "ip", "os", "platform_id",
|
||||
|
@ -240,7 +254,10 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
|||
node.assets.remove(*assets)
|
||||
|
||||
# 把孤儿资产添加到 root 节点
|
||||
orphan_assets = Asset.objects.filter(id__in=[a.id for a in assets], nodes__isnull=True).distinct()
|
||||
orphan_assets = Asset.objects.filter(
|
||||
id__in=[a.id for a in assets],
|
||||
nodes__isnull=True
|
||||
).distinct()
|
||||
Node.org_root().assets.add(*orphan_assets)
|
||||
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ def compute_parent_key(key):
|
|||
class NodeQuerySet(models.QuerySet):
|
||||
def delete(self):
|
||||
raise NotImplementedError
|
||||
|
||||
#
|
||||
|
||||
class FamilyMixin:
|
||||
__parents = None
|
||||
|
@ -446,8 +446,9 @@ class SomeNodesMixin:
|
|||
|
||||
@classmethod
|
||||
def default_node(cls):
|
||||
with tmp_to_org(Organization.default()):
|
||||
defaults = {'value': cls.default_value}
|
||||
default_org = Organization.default()
|
||||
with tmp_to_org(default_org):
|
||||
defaults = {'value': default_org.name}
|
||||
try:
|
||||
obj, created = cls.objects.get_or_create(
|
||||
defaults=defaults, key=cls.default_key,
|
||||
|
@ -482,25 +483,34 @@ class SomeNodesMixin:
|
|||
|
||||
@classmethod
|
||||
def create_org_root_node(cls):
|
||||
# 如果使用current_org 在set_current_org时会死循环
|
||||
ori_org = get_current_org()
|
||||
with transaction.atomic():
|
||||
if not ori_org.is_real():
|
||||
return cls.default_node()
|
||||
key = cls.get_next_org_root_node_key()
|
||||
root = cls.objects.create(key=key, value=ori_org.name)
|
||||
return root
|
||||
|
||||
@classmethod
|
||||
def org_root(cls):
|
||||
root = cls.objects.filter(parent_key='')\
|
||||
.filter(key__regex=r'^[0-9]+$')\
|
||||
.exclude(key__startswith='-')\
|
||||
def org_root_nodes(cls):
|
||||
nodes = cls.objects.filter(parent_key='') \
|
||||
.filter(key__regex=r'^[0-9]+$') \
|
||||
.exclude(key__startswith='-') \
|
||||
.order_by('key')
|
||||
if root:
|
||||
return root[0]
|
||||
return nodes
|
||||
|
||||
@classmethod
|
||||
def org_root(cls):
|
||||
org_roots = cls.org_root_nodes()
|
||||
if org_roots:
|
||||
return org_roots[0]
|
||||
ori_org = get_current_org()
|
||||
# 如果使用current_org 在set_current_org时会死循环
|
||||
if ori_org.is_root():
|
||||
root = cls.default_node()
|
||||
elif ori_org.is_default():
|
||||
root = cls.default_node()
|
||||
else:
|
||||
return cls.create_org_root_node()
|
||||
root = cls.create_org_root_node()
|
||||
return root
|
||||
|
||||
@classmethod
|
||||
def initial_some_nodes(cls):
|
||||
|
@ -519,9 +529,6 @@ class SomeNodesMixin:
|
|||
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):
|
||||
|
|
|
@ -13,18 +13,20 @@ logger = get_logger(__file__)
|
|||
|
||||
|
||||
@shared_task
|
||||
def check_node_assets_amount_task(orgid=None):
|
||||
if orgid is None:
|
||||
orgs = [*Organization.objects.all(), Organization.default()]
|
||||
def check_node_assets_amount_task(org_id=None):
|
||||
if org_id is None:
|
||||
orgs = Organization.objects.all()
|
||||
else:
|
||||
orgs = [Organization.get_instance(orgid)]
|
||||
orgs = [Organization.get_instance(org_id)]
|
||||
|
||||
for org in orgs:
|
||||
try:
|
||||
with tmp_to_org(org):
|
||||
check_node_assets_amount()
|
||||
except AcquireFailed:
|
||||
logger.error(_('The task of self-checking is already running and cannot be started repeatedly'))
|
||||
error = _('The task of self-checking is already running '
|
||||
'and cannot be started repeatedly')
|
||||
logger.error(error)
|
||||
|
||||
|
||||
@register_as_period_task(crontab='0 2 * * *')
|
||||
|
|
|
@ -110,12 +110,17 @@ class PermissionsMixin(UserPassesTestMixin):
|
|||
return True
|
||||
|
||||
|
||||
class UserCanUpdatePassword:
|
||||
class UserCanUseCurrentOrg(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return current_org.can_use_by(request.user)
|
||||
|
||||
|
||||
class UserCanUpdatePassword(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return request.user.can_update_password()
|
||||
|
||||
|
||||
class UserCanUpdateSSHKey:
|
||||
class UserCanUpdateSSHKey(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return request.user.can_update_ssh_key()
|
||||
|
||||
|
|
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: 2021-01-26 17:25+0800\n"
|
||||
"POT-Creation-Date: 2021-02-20 15:17+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"
|
||||
|
@ -32,8 +32,8 @@ msgstr "远程应用"
|
|||
msgid "Custom"
|
||||
msgstr "自定义"
|
||||
|
||||
#: applications/models/application.py:10 assets/models/asset.py:149
|
||||
#: assets/models/base.py:234 assets/models/cluster.py:18
|
||||
#: applications/models/application.py:10 assets/models/asset.py:142
|
||||
#: assets/models/base.py:235 assets/models/cluster.py:18
|
||||
#: assets/models/cmd_filter.py:21 assets/models/domain.py:21
|
||||
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
|
||||
#: orgs/models.py:23 perms/models/base.py:48 settings/models.py:29
|
||||
|
@ -78,7 +78,7 @@ msgstr "类别"
|
|||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
||||
#: applications/models/application.py:19 assets/models/asset.py:198
|
||||
#: applications/models/application.py:19 assets/models/asset.py:191
|
||||
#: assets/models/domain.py:27 assets/models/domain.py:55
|
||||
msgid "Domain"
|
||||
msgstr "网域"
|
||||
|
@ -89,8 +89,8 @@ msgstr ""
|
|||
|
||||
# msgid "Date created"
|
||||
# msgstr "创建日期"
|
||||
#: applications/models/application.py:23 assets/models/asset.py:154
|
||||
#: assets/models/asset.py:230 assets/models/base.py:239
|
||||
#: applications/models/application.py:23 assets/models/asset.py:147
|
||||
#: assets/models/asset.py:223 assets/models/base.py:240
|
||||
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:23
|
||||
#: assets/models/cmd_filter.py:57 assets/models/domain.py:22
|
||||
#: assets/models/domain.py:56 assets/models/group.py:23
|
||||
|
@ -125,16 +125,16 @@ msgstr "主机"
|
|||
#: applications/serializers/attrs/application_type/mysql_workbench.py:22
|
||||
#: applications/serializers/attrs/application_type/oracle.py:11
|
||||
#: applications/serializers/attrs/application_type/pgsql.py:11
|
||||
#: assets/models/asset.py:195 assets/models/domain.py:53
|
||||
#: assets/models/asset.py:188 assets/models/domain.py:53
|
||||
msgid "Port"
|
||||
msgstr "端口"
|
||||
|
||||
#: applications/serializers/attrs/application_category/remote_app.py:33
|
||||
#: assets/models/asset.py:363 assets/models/authbook.py:26
|
||||
#: assets/models/asset.py:355 assets/models/authbook.py:26
|
||||
#: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:32
|
||||
#: assets/serializers/asset_user.py:47 assets/serializers/asset_user.py:84
|
||||
#: assets/serializers/system_user.py:191 audits/models.py:38
|
||||
#: perms/models/asset_permission.py:96 templates/index.html:82
|
||||
#: perms/models/asset_permission.py:99 templates/index.html:82
|
||||
#: terminal/backends/command/models.py:19
|
||||
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:39
|
||||
#: users/templates/users/user_asset_permission.html:40
|
||||
|
@ -161,7 +161,7 @@ msgstr "目标URL"
|
|||
#: applications/serializers/attrs/application_type/custom.py:21
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:30
|
||||
#: applications/serializers/attrs/application_type/vmware_client.py:26
|
||||
#: assets/models/base.py:235 assets/models/gathered_user.py:15
|
||||
#: assets/models/base.py:236 assets/models/gathered_user.py:15
|
||||
#: audits/models.py:99 authentication/forms.py:11
|
||||
#: authentication/templates/authentication/login.html:101
|
||||
#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:515
|
||||
|
@ -178,7 +178,7 @@ msgstr "用户名"
|
|||
#: applications/serializers/attrs/application_type/custom.py:25
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:34
|
||||
#: applications/serializers/attrs/application_type/vmware_client.py:30
|
||||
#: assets/models/base.py:236 assets/serializers/asset_user.py:71
|
||||
#: assets/models/base.py:237 assets/serializers/asset_user.py:71
|
||||
#: audits/signals_handler.py:42 authentication/forms.py:13
|
||||
#: authentication/templates/authentication/login.html:109
|
||||
#: settings/serializers/settings.py:84 users/forms/user.py:22
|
||||
|
@ -204,7 +204,7 @@ msgid "Target url"
|
|||
msgstr "目标URL"
|
||||
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:18
|
||||
#: assets/models/asset.py:190 assets/models/domain.py:52
|
||||
#: assets/models/asset.py:183 assets/models/domain.py:52
|
||||
#: assets/serializers/asset_user.py:46 settings/serializers/settings.py:103
|
||||
#: users/templates/users/_granted_assets.html:26
|
||||
#: users/templates/users/user_asset_permission.html:156
|
||||
|
@ -219,15 +219,15 @@ msgstr "删除失败,存在关联资产"
|
|||
msgid "Number required"
|
||||
msgstr "需要为数字"
|
||||
|
||||
#: assets/api/node.py:67
|
||||
#: assets/api/node.py:60
|
||||
msgid "You can't update the root node name"
|
||||
msgstr "不能修改根节点名称"
|
||||
|
||||
#: assets/api/node.py:74
|
||||
#: assets/api/node.py:67
|
||||
msgid "You can't delete the root node ({})"
|
||||
msgstr "不能删除根节点 ({})"
|
||||
|
||||
#: assets/api/node.py:77
|
||||
#: assets/api/node.py:70
|
||||
msgid "Deletion failed and the node contains children or assets"
|
||||
msgstr "删除失败,节点包含子节点或资产"
|
||||
|
||||
|
@ -239,137 +239,137 @@ msgstr "不能移除资产的管理用户账号"
|
|||
msgid "Latest version could not be delete"
|
||||
msgstr "最新版本的不能被删除"
|
||||
|
||||
#: assets/models/asset.py:150 xpack/plugins/cloud/providers/base.py:17
|
||||
#: assets/models/asset.py:143 xpack/plugins/cloud/providers/base.py:17
|
||||
msgid "Base"
|
||||
msgstr "基础"
|
||||
|
||||
#: assets/models/asset.py:151
|
||||
#: assets/models/asset.py:144
|
||||
msgid "Charset"
|
||||
msgstr "编码"
|
||||
|
||||
#: assets/models/asset.py:152 tickets/models/ticket.py:40
|
||||
#: assets/models/asset.py:145 tickets/models/ticket.py:40
|
||||
msgid "Meta"
|
||||
msgstr "元数据"
|
||||
|
||||
#: assets/models/asset.py:153
|
||||
#: assets/models/asset.py:146
|
||||
msgid "Internal"
|
||||
msgstr "内部的"
|
||||
|
||||
#: assets/models/asset.py:173 assets/models/asset.py:197
|
||||
#: assets/models/asset.py:166 assets/models/asset.py:190
|
||||
#: assets/serializers/asset.py:66
|
||||
msgid "Platform"
|
||||
msgstr "系统平台"
|
||||
|
||||
#: assets/models/asset.py:191 assets/serializers/asset_user.py:45
|
||||
#: assets/models/asset.py:184 assets/serializers/asset_user.py:45
|
||||
#: assets/serializers/gathered_user.py:20 settings/serializers/settings.py:102
|
||||
#: users/templates/users/_granted_assets.html:25
|
||||
#: users/templates/users/user_asset_permission.html:157
|
||||
msgid "Hostname"
|
||||
msgstr "主机名"
|
||||
|
||||
#: assets/models/asset.py:194 assets/models/domain.py:54
|
||||
#: assets/models/asset.py:187 assets/models/domain.py:54
|
||||
#: assets/models/user.py:120 terminal/serializers/session.py:29
|
||||
#: terminal/serializers/storage.py:69
|
||||
msgid "Protocol"
|
||||
msgstr "协议"
|
||||
|
||||
#: assets/models/asset.py:196 assets/serializers/asset.py:68
|
||||
#: assets/models/asset.py:189 assets/serializers/asset.py:68
|
||||
#: perms/serializers/asset/user_permission.py:41
|
||||
msgid "Protocols"
|
||||
msgstr "协议组"
|
||||
|
||||
#: assets/models/asset.py:199 assets/models/user.py:115
|
||||
#: perms/models/asset_permission.py:97
|
||||
#: assets/models/asset.py:192 assets/models/user.py:115
|
||||
#: perms/models/asset_permission.py:100
|
||||
#: xpack/plugins/change_auth_plan/models.py:56
|
||||
#: xpack/plugins/gathered_user/models.py:24
|
||||
msgid "Nodes"
|
||||
msgstr "节点"
|
||||
|
||||
#: assets/models/asset.py:200 assets/models/cmd_filter.py:22
|
||||
#: assets/models/asset.py:193 assets/models/cmd_filter.py:22
|
||||
#: assets/models/domain.py:57 assets/models/label.py:22
|
||||
#: authentication/models.py:46
|
||||
msgid "Is active"
|
||||
msgstr "激活"
|
||||
|
||||
#: assets/models/asset.py:203 assets/models/cluster.py:19
|
||||
#: assets/models/asset.py:196 assets/models/cluster.py:19
|
||||
#: assets/models/user.py:66 templates/_nav.html:44
|
||||
#: xpack/plugins/cloud/models.py:143 xpack/plugins/cloud/serializers.py:137
|
||||
msgid "Admin user"
|
||||
msgstr "管理用户"
|
||||
|
||||
#: assets/models/asset.py:206
|
||||
#: assets/models/asset.py:199
|
||||
msgid "Public IP"
|
||||
msgstr "公网IP"
|
||||
|
||||
#: assets/models/asset.py:207
|
||||
#: assets/models/asset.py:200
|
||||
msgid "Asset number"
|
||||
msgstr "资产编号"
|
||||
|
||||
#: assets/models/asset.py:210
|
||||
#: assets/models/asset.py:203
|
||||
msgid "Vendor"
|
||||
msgstr "制造商"
|
||||
|
||||
#: assets/models/asset.py:211
|
||||
#: assets/models/asset.py:204
|
||||
msgid "Model"
|
||||
msgstr "型号"
|
||||
|
||||
#: assets/models/asset.py:212
|
||||
#: assets/models/asset.py:205
|
||||
msgid "Serial number"
|
||||
msgstr "序列号"
|
||||
|
||||
#: assets/models/asset.py:214
|
||||
#: assets/models/asset.py:207
|
||||
msgid "CPU model"
|
||||
msgstr "CPU型号"
|
||||
|
||||
#: assets/models/asset.py:215
|
||||
#: assets/models/asset.py:208
|
||||
msgid "CPU count"
|
||||
msgstr "CPU数量"
|
||||
|
||||
#: assets/models/asset.py:216
|
||||
#: assets/models/asset.py:209
|
||||
msgid "CPU cores"
|
||||
msgstr "CPU核数"
|
||||
|
||||
#: assets/models/asset.py:217
|
||||
#: assets/models/asset.py:210
|
||||
msgid "CPU vcpus"
|
||||
msgstr "CPU总数"
|
||||
|
||||
#: assets/models/asset.py:218
|
||||
#: assets/models/asset.py:211
|
||||
msgid "Memory"
|
||||
msgstr "内存"
|
||||
|
||||
#: assets/models/asset.py:219
|
||||
#: assets/models/asset.py:212
|
||||
msgid "Disk total"
|
||||
msgstr "硬盘大小"
|
||||
|
||||
#: assets/models/asset.py:220
|
||||
#: assets/models/asset.py:213
|
||||
msgid "Disk info"
|
||||
msgstr "硬盘信息"
|
||||
|
||||
#: assets/models/asset.py:222
|
||||
#: assets/models/asset.py:215
|
||||
msgid "OS"
|
||||
msgstr "操作系统"
|
||||
|
||||
#: assets/models/asset.py:223
|
||||
#: assets/models/asset.py:216
|
||||
msgid "OS version"
|
||||
msgstr "系统版本"
|
||||
|
||||
#: assets/models/asset.py:224
|
||||
#: assets/models/asset.py:217
|
||||
msgid "OS arch"
|
||||
msgstr "系统架构"
|
||||
|
||||
#: assets/models/asset.py:225
|
||||
#: assets/models/asset.py:218
|
||||
msgid "Hostname raw"
|
||||
msgstr "主机名原始"
|
||||
|
||||
#: assets/models/asset.py:227 templates/_nav.html:46
|
||||
#: assets/models/asset.py:220 templates/_nav.html:46
|
||||
msgid "Labels"
|
||||
msgstr "标签管理"
|
||||
|
||||
#: assets/models/asset.py:228 assets/models/base.py:242
|
||||
#: assets/models/asset.py:221 assets/models/base.py:243
|
||||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
|
||||
#: assets/models/cmd_filter.py:60 assets/models/group.py:21
|
||||
#: common/db/models.py:67 common/mixins/models.py:49 orgs/models.py:24
|
||||
#: orgs/models.py:427 perms/models/base.py:54 users/models/user.py:558
|
||||
#: orgs/models.py:412 perms/models/base.py:54 users/models/user.py:558
|
||||
#: users/serializers/group.py:35 users/templates/users/user_detail.html:97
|
||||
#: xpack/plugins/change_auth_plan/models.py:81 xpack/plugins/cloud/models.py:58
|
||||
#: xpack/plugins/cloud/models.py:156 xpack/plugins/gathered_user/models.py:30
|
||||
|
@ -378,12 +378,12 @@ msgstr "创建者"
|
|||
|
||||
# msgid "Created by"
|
||||
# msgstr "创建者"
|
||||
#: assets/models/asset.py:229 assets/models/base.py:240
|
||||
#: assets/models/asset.py:222 assets/models/base.py:241
|
||||
#: assets/models/cluster.py:26 assets/models/domain.py:24
|
||||
#: assets/models/gathered_user.py:19 assets/models/group.py:22
|
||||
#: assets/models/label.py:25 common/db/models.py:69 common/mixins/models.py:50
|
||||
#: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:25
|
||||
#: orgs/models.py:425 perms/models/base.py:55 users/models/group.py:18
|
||||
#: orgs/models.py:410 perms/models/base.py:55 users/models/group.py:18
|
||||
#: users/templates/users/user_group_detail.html:58
|
||||
#: xpack/plugins/cloud/models.py:61 xpack/plugins/cloud/models.py:159
|
||||
msgid "Date created"
|
||||
|
@ -405,21 +405,21 @@ msgstr "版本"
|
|||
msgid "AuthBook"
|
||||
msgstr ""
|
||||
|
||||
#: assets/models/base.py:237 xpack/plugins/change_auth_plan/models.py:72
|
||||
#: assets/models/base.py:238 xpack/plugins/change_auth_plan/models.py:72
|
||||
#: xpack/plugins/change_auth_plan/models.py:197
|
||||
#: xpack/plugins/change_auth_plan/models.py:292
|
||||
msgid "SSH private key"
|
||||
msgstr "SSH密钥"
|
||||
|
||||
#: assets/models/base.py:238 xpack/plugins/change_auth_plan/models.py:75
|
||||
#: assets/models/base.py:239 xpack/plugins/change_auth_plan/models.py:75
|
||||
#: xpack/plugins/change_auth_plan/models.py:193
|
||||
#: xpack/plugins/change_auth_plan/models.py:288
|
||||
msgid "SSH public key"
|
||||
msgstr "SSH公钥"
|
||||
|
||||
#: assets/models/base.py:241 assets/models/gathered_user.py:20
|
||||
#: assets/models/base.py:242 assets/models/gathered_user.py:20
|
||||
#: common/db/models.py:70 common/mixins/models.py:51 ops/models/adhoc.py:39
|
||||
#: orgs/models.py:426
|
||||
#: orgs/models.py:411
|
||||
msgid "Date updated"
|
||||
msgstr "更新日期"
|
||||
|
||||
|
@ -556,9 +556,9 @@ msgstr "默认资产组"
|
|||
|
||||
#: assets/models/label.py:15 audits/models.py:36 audits/models.py:56
|
||||
#: audits/models.py:69 audits/serializers.py:81 authentication/models.py:44
|
||||
#: authentication/models.py:95 orgs/models.py:18 orgs/models.py:423
|
||||
#: perms/models/asset_permission.py:173 perms/models/base.py:49
|
||||
#: templates/index.html:78 terminal/backends/command/models.py:18
|
||||
#: authentication/models.py:95 orgs/models.py:18 orgs/models.py:408
|
||||
#: perms/models/base.py:49 templates/index.html:78
|
||||
#: terminal/backends/command/models.py:18
|
||||
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:37
|
||||
#: tickets/models/comment.py:17 users/forms/group.py:15
|
||||
#: users/models/user.py:158 users/models/user.py:665
|
||||
|
@ -575,31 +575,31 @@ msgstr "默认资产组"
|
|||
msgid "User"
|
||||
msgstr "用户"
|
||||
|
||||
#: assets/models/label.py:19 assets/models/node.py:413 settings/models.py:30
|
||||
#: assets/models/label.py:19 assets/models/node.py:545 settings/models.py:30
|
||||
msgid "Value"
|
||||
msgstr "值"
|
||||
|
||||
#: assets/models/node.py:143
|
||||
#: assets/models/node.py:152
|
||||
msgid "New node"
|
||||
msgstr "新节点"
|
||||
|
||||
#: assets/models/node.py:316 users/templates/users/_granted_assets.html:130
|
||||
#: assets/models/node.py:450 users/templates/users/_granted_assets.html:130
|
||||
msgid "empty"
|
||||
msgstr "空"
|
||||
|
||||
#: assets/models/node.py:412 perms/models/asset_permission.py:148
|
||||
#: assets/models/node.py:544 perms/models/asset_permission.py:156
|
||||
msgid "Key"
|
||||
msgstr "键"
|
||||
|
||||
#: assets/models/node.py:414
|
||||
#: assets/models/node.py:546
|
||||
msgid "Full value"
|
||||
msgstr "全称"
|
||||
|
||||
#: assets/models/node.py:417 perms/models/asset_permission.py:152
|
||||
#: assets/models/node.py:549 perms/models/asset_permission.py:157
|
||||
msgid "Parent key"
|
||||
msgstr "ssh私钥"
|
||||
|
||||
#: assets/models/node.py:426 assets/serializers/system_user.py:190
|
||||
#: assets/models/node.py:557 assets/serializers/system_user.py:190
|
||||
#: users/templates/users/user_asset_permission.html:41
|
||||
#: users/templates/users/user_asset_permission.html:73
|
||||
#: users/templates/users/user_asset_permission.html:158
|
||||
|
@ -668,7 +668,7 @@ msgstr "用户组"
|
|||
|
||||
#: assets/models/user.py:221 audits/models.py:39
|
||||
#: perms/models/application_permission.py:31
|
||||
#: perms/models/asset_permission.py:98 templates/_nav.html:45
|
||||
#: perms/models/asset_permission.py:101 templates/_nav.html:45
|
||||
#: terminal/backends/command/models.py:20
|
||||
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:41
|
||||
#: users/templates/users/_granted_assets.html:27
|
||||
|
@ -727,7 +727,7 @@ msgstr "硬件信息"
|
|||
msgid "Org name"
|
||||
msgstr "组织名称"
|
||||
|
||||
#: assets/serializers/asset.py:162 assets/serializers/asset.py:201
|
||||
#: assets/serializers/asset.py:162 assets/serializers/asset.py:194
|
||||
msgid "Connectivity"
|
||||
msgstr "连接"
|
||||
|
||||
|
@ -873,11 +873,6 @@ msgstr "更新节点资产硬件信息: {}"
|
|||
msgid "Gather assets users"
|
||||
msgstr "收集资产上的用户"
|
||||
|
||||
#: assets/tasks/nodes_amount.py:21
|
||||
msgid ""
|
||||
"The task of self-checking is already running and cannot be started repeatedly"
|
||||
msgstr "自检程序已经在运行,不能重复启动"
|
||||
|
||||
#: assets/tasks/push_system_user.py:184
|
||||
#: assets/tasks/system_user_connectivity.py:89
|
||||
msgid "System user is dynamic: {}"
|
||||
|
@ -1081,7 +1076,7 @@ msgstr "用户代理"
|
|||
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
|
||||
#: authentication/templates/authentication/login_otp.html:6
|
||||
#: users/forms/profile.py:52 users/models/user.py:539
|
||||
#: users/serializers/user.py:232 users/templates/users/user_detail.html:77
|
||||
#: users/serializers/profile.py:102 users/templates/users/user_detail.html:77
|
||||
#: users/templates/users/user_profile.html:87
|
||||
msgid "MFA"
|
||||
msgstr "多因子认证"
|
||||
|
@ -1355,7 +1350,7 @@ msgid "Show"
|
|||
msgstr "显示"
|
||||
|
||||
#: authentication/templates/authentication/_access_key_modal.html:66
|
||||
#: users/models/user.py:443 users/serializers/user.py:229
|
||||
#: users/models/user.py:443 users/serializers/profile.py:99
|
||||
#: users/templates/users/user_profile.html:94
|
||||
#: users/templates/users/user_profile.html:163
|
||||
#: users/templates/users/user_profile.html:166
|
||||
|
@ -1364,7 +1359,7 @@ msgid "Disable"
|
|||
msgstr "禁用"
|
||||
|
||||
#: authentication/templates/authentication/_access_key_modal.html:67
|
||||
#: users/models/user.py:444 users/serializers/user.py:230
|
||||
#: users/models/user.py:444 users/serializers/profile.py:100
|
||||
#: users/templates/users/user_profile.html:92
|
||||
#: users/templates/users/user_profile.html:170
|
||||
msgid "Enable"
|
||||
|
@ -1605,7 +1600,7 @@ msgstr "不能包含特殊字符"
|
|||
msgid "<h1>Flow service unavailable, check it</h1>"
|
||||
msgstr ""
|
||||
|
||||
#: jumpserver/views/other.py:26
|
||||
#: jumpserver/views/other.py:27
|
||||
msgid ""
|
||||
"<div>Luna is a separately deployed program, you need to deploy Luna, koko, "
|
||||
"configure nginx for url distribution,</div> </div>If you see this page, "
|
||||
|
@ -1614,11 +1609,11 @@ msgstr ""
|
|||
"<div>Luna是单独部署的一个程序,你需要部署luna,koko, </div><div>如果你看到了"
|
||||
"这个页面,证明你访问的不是nginx监听的端口,祝你好运</div>"
|
||||
|
||||
#: jumpserver/views/other.py:77
|
||||
#: jumpserver/views/other.py:78
|
||||
msgid "Websocket server run on port: {}, you should proxy it on nginx"
|
||||
msgstr "Websocket 服务运行在端口: {}, 请检查nginx是否代理是否设置"
|
||||
|
||||
#: jumpserver/views/other.py:91
|
||||
#: jumpserver/views/other.py:92
|
||||
msgid ""
|
||||
"<div>Koko is a separately deployed program, you need to deploy Koko, "
|
||||
"configure nginx for url distribution,</div> </div>If you see this page, "
|
||||
|
@ -1795,8 +1790,8 @@ msgstr "组织包含未删除的资源"
|
|||
msgid "The current organization cannot be deleted"
|
||||
msgstr "当前组织不能被删除"
|
||||
|
||||
#: orgs/mixins/models.py:56 orgs/mixins/serializers.py:25 orgs/models.py:41
|
||||
#: orgs/models.py:422 orgs/serializers.py:100
|
||||
#: orgs/mixins/models.py:53 orgs/mixins/serializers.py:25 orgs/models.py:39
|
||||
#: orgs/models.py:407 orgs/serializers.py:100
|
||||
#: tickets/serializers/ticket/ticket.py:81
|
||||
msgid "Organization"
|
||||
msgstr "组织"
|
||||
|
@ -1809,7 +1804,11 @@ msgstr "组织管理员"
|
|||
msgid "Organization auditor"
|
||||
msgstr "组织审计员"
|
||||
|
||||
#: orgs/models.py:424 users/forms/user.py:27 users/models/user.py:527
|
||||
#: orgs/models.py:33
|
||||
msgid "GLOBAL"
|
||||
msgstr "全局组织"
|
||||
|
||||
#: orgs/models.py:409 users/forms/user.py:27 users/models/user.py:527
|
||||
#: users/templates/users/_select_user_modal.html:15
|
||||
#: users/templates/users/user_detail.html:73
|
||||
#: users/templates/users/user_list.html:16
|
||||
|
@ -1817,7 +1816,7 @@ msgstr "组织审计员"
|
|||
msgid "Role"
|
||||
msgstr "角色"
|
||||
|
||||
#: perms/const.py:7 perms/utils/asset/user_permission.py:28
|
||||
#: perms/const.py:7 perms/models/asset_permission.py:189
|
||||
msgid "Ungrouped"
|
||||
msgstr "未分组"
|
||||
|
||||
|
@ -1841,47 +1840,52 @@ msgstr "应用程序"
|
|||
msgid "Application permission"
|
||||
msgstr "应用管理"
|
||||
|
||||
#: perms/models/asset_permission.py:34 settings/serializers/settings.py:107
|
||||
#: perms/models/asset_permission.py:37 settings/serializers/settings.py:107
|
||||
msgid "All"
|
||||
msgstr "全部"
|
||||
|
||||
#: perms/models/asset_permission.py:35
|
||||
#: perms/models/asset_permission.py:38
|
||||
msgid "Connect"
|
||||
msgstr "连接"
|
||||
|
||||
#: perms/models/asset_permission.py:36
|
||||
#: perms/models/asset_permission.py:39
|
||||
msgid "Upload file"
|
||||
msgstr "上传文件"
|
||||
|
||||
#: perms/models/asset_permission.py:37
|
||||
#: perms/models/asset_permission.py:40
|
||||
msgid "Download file"
|
||||
msgstr "下载文件"
|
||||
|
||||
#: perms/models/asset_permission.py:38
|
||||
#: perms/models/asset_permission.py:41
|
||||
msgid "Upload download"
|
||||
msgstr "上传下载"
|
||||
|
||||
#: perms/models/asset_permission.py:39
|
||||
#: perms/models/asset_permission.py:42
|
||||
msgid "Clipboard copy"
|
||||
msgstr "剪贴板复制"
|
||||
|
||||
#: perms/models/asset_permission.py:40
|
||||
#: perms/models/asset_permission.py:43
|
||||
msgid "Clipboard paste"
|
||||
msgstr "剪贴板粘贴"
|
||||
|
||||
#: perms/models/asset_permission.py:41
|
||||
#: perms/models/asset_permission.py:44
|
||||
msgid "Clipboard copy paste"
|
||||
msgstr "剪贴板复制粘贴"
|
||||
|
||||
#: perms/models/asset_permission.py:99 perms/serializers/asset/permission.py:60
|
||||
#: perms/models/asset_permission.py:102
|
||||
#: perms/serializers/asset/permission.py:60
|
||||
msgid "Actions"
|
||||
msgstr "动作"
|
||||
|
||||
#: perms/models/asset_permission.py:103 templates/_nav.html:78
|
||||
#: perms/models/asset_permission.py:106 templates/_nav.html:78
|
||||
#: users/templates/users/_user_detail_nav_header.html:31
|
||||
msgid "Asset permission"
|
||||
msgstr "资产授权"
|
||||
|
||||
#: perms/models/asset_permission.py:191
|
||||
msgid "Favorite"
|
||||
msgstr "收藏夹"
|
||||
|
||||
#: perms/models/base.py:50 templates/_nav.html:21 users/forms/user.py:168
|
||||
#: users/models/group.py:31 users/models/user.py:523
|
||||
#: users/templates/users/_select_user_modal.html:16
|
||||
|
@ -1912,11 +1916,11 @@ msgid ""
|
|||
"permission type. ({})"
|
||||
msgstr "应用列表中包含与授权类型不同的应用。({})"
|
||||
|
||||
#: perms/serializers/asset/permission.py:58 users/serializers/user.py:80
|
||||
#: perms/serializers/asset/permission.py:58 users/serializers/user.py:65
|
||||
msgid "Is expired"
|
||||
msgstr "是否过期"
|
||||
|
||||
#: perms/serializers/asset/permission.py:59 users/serializers/user.py:79
|
||||
#: perms/serializers/asset/permission.py:59 users/serializers/user.py:64
|
||||
msgid "Is valid"
|
||||
msgstr "账户是否有效"
|
||||
|
||||
|
@ -1932,14 +1936,6 @@ msgstr "用户组数量"
|
|||
msgid "System users amount"
|
||||
msgstr "系统用户数量"
|
||||
|
||||
#: perms/utils/asset/user_permission.py:30
|
||||
msgid "Favorite"
|
||||
msgstr "收藏夹"
|
||||
|
||||
#: perms/utils/asset/user_permission.py:522
|
||||
msgid "Please wait while your data is being initialized"
|
||||
msgstr "数据正在初始化,请稍等"
|
||||
|
||||
#: settings/api/common.py:24
|
||||
msgid "Test mail sent to {}, please check"
|
||||
msgstr "邮件已经发送{}, 请检查"
|
||||
|
@ -3054,7 +3050,7 @@ msgstr "索引"
|
|||
msgid "Doc type"
|
||||
msgstr "文档类型"
|
||||
|
||||
#: terminal/serializers/terminal.py:44 terminal/serializers/terminal.py:52
|
||||
#: terminal/serializers/terminal.py:47 terminal/serializers/terminal.py:55
|
||||
msgid "Not found"
|
||||
msgstr "没有发现"
|
||||
|
||||
|
@ -3530,8 +3526,8 @@ msgid "Public key should not be the same as your old one."
|
|||
msgstr "不能和原来的密钥相同"
|
||||
|
||||
#: users/forms/profile.py:137 users/forms/user.py:90
|
||||
#: users/serializers/user.py:192 users/serializers/user.py:277
|
||||
#: users/serializers/user.py:335
|
||||
#: users/serializers/profile.py:74 users/serializers/profile.py:147
|
||||
#: users/serializers/profile.py:160
|
||||
msgid "Not a valid ssh public key"
|
||||
msgstr "SSH密钥不合法"
|
||||
|
||||
|
@ -3555,15 +3551,15 @@ msgstr "添加到用户组"
|
|||
msgid "* Your password does not meet the requirements"
|
||||
msgstr "* 您的密码不符合要求"
|
||||
|
||||
#: users/forms/user.py:124 users/serializers/user.py:35
|
||||
#: users/forms/user.py:124 users/serializers/user.py:20
|
||||
msgid "Reset link will be generated and sent to the user"
|
||||
msgstr "生成重置密码链接,通过邮件发送给用户"
|
||||
|
||||
#: users/forms/user.py:125 users/serializers/user.py:36
|
||||
#: users/forms/user.py:125 users/serializers/user.py:21
|
||||
msgid "Set password"
|
||||
msgstr "设置密码"
|
||||
|
||||
#: users/forms/user.py:132 users/serializers/user.py:43
|
||||
#: users/forms/user.py:132 users/serializers/user.py:28
|
||||
#: xpack/plugins/change_auth_plan/models.py:61
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:30
|
||||
msgid "Password strategy"
|
||||
|
@ -3605,75 +3601,75 @@ msgstr "管理员"
|
|||
msgid "Administrator is the super user of system"
|
||||
msgstr "Administrator是初始的超级管理员"
|
||||
|
||||
#: users/serializers/user.py:45
|
||||
msgid "MFA level for display"
|
||||
msgstr "多因子认证等级(显示名称)"
|
||||
|
||||
#: users/serializers/user.py:46
|
||||
msgid "Login blocked"
|
||||
msgstr "登录被阻塞"
|
||||
|
||||
#: users/serializers/user.py:47
|
||||
msgid "Can update"
|
||||
msgstr "是否可更新"
|
||||
|
||||
#: users/serializers/user.py:48
|
||||
msgid "Can delete"
|
||||
msgstr "是否可删除"
|
||||
|
||||
#: users/serializers/user.py:49 users/serializers/user.py:85
|
||||
msgid "Organization role name"
|
||||
msgstr "组织角色名称"
|
||||
|
||||
#: users/serializers/user.py:78 users/serializers/user.py:248
|
||||
msgid "Is first login"
|
||||
msgstr "首次登录"
|
||||
|
||||
#: users/serializers/user.py:81
|
||||
msgid "Avatar url"
|
||||
msgstr "头像路径"
|
||||
|
||||
#: users/serializers/user.py:83
|
||||
msgid "Groups name"
|
||||
msgstr "用户组名"
|
||||
|
||||
#: users/serializers/user.py:84
|
||||
msgid "Source name"
|
||||
msgstr "用户来源名"
|
||||
|
||||
#: users/serializers/user.py:86
|
||||
msgid "Super role name"
|
||||
msgstr "超级角色名称"
|
||||
|
||||
#: users/serializers/user.py:87
|
||||
msgid "Total role name"
|
||||
msgstr "汇总角色名称"
|
||||
|
||||
#: users/serializers/user.py:88
|
||||
msgid "MFA enabled"
|
||||
msgstr "是否开启多因子认证"
|
||||
|
||||
#: users/serializers/user.py:89
|
||||
msgid "MFA force enabled"
|
||||
msgstr "强制启用多因子认证"
|
||||
|
||||
#: users/serializers/user.py:112
|
||||
msgid "Role limit to {}"
|
||||
msgstr "角色只能为 {}"
|
||||
|
||||
#: users/serializers/user.py:124 users/serializers/user.py:301
|
||||
msgid "Password does not match security rules"
|
||||
msgstr "密码不满足安全规则"
|
||||
|
||||
#: users/serializers/user.py:293
|
||||
#: users/serializers/profile.py:32
|
||||
msgid "The old password is incorrect"
|
||||
msgstr "旧密码错误"
|
||||
|
||||
#: users/serializers/user.py:307
|
||||
#: users/serializers/profile.py:40 users/serializers/user.py:109
|
||||
msgid "Password does not match security rules"
|
||||
msgstr "密码不满足安全规则"
|
||||
|
||||
#: users/serializers/profile.py:46
|
||||
msgid "The newly set password is inconsistent"
|
||||
msgstr "两次密码不一致"
|
||||
|
||||
#: users/serializers_v2/user.py:36
|
||||
#: users/serializers/profile.py:118 users/serializers/user.py:63
|
||||
msgid "Is first login"
|
||||
msgstr "首次登录"
|
||||
|
||||
#: users/serializers/user.py:30
|
||||
msgid "MFA level for display"
|
||||
msgstr "多因子认证等级(显示名称)"
|
||||
|
||||
#: users/serializers/user.py:31
|
||||
msgid "Login blocked"
|
||||
msgstr "登录被阻塞"
|
||||
|
||||
#: users/serializers/user.py:32
|
||||
msgid "Can update"
|
||||
msgstr "是否可更新"
|
||||
|
||||
#: users/serializers/user.py:33
|
||||
msgid "Can delete"
|
||||
msgstr "是否可删除"
|
||||
|
||||
#: users/serializers/user.py:34 users/serializers/user.py:70
|
||||
msgid "Organization role name"
|
||||
msgstr "组织角色名称"
|
||||
|
||||
#: users/serializers/user.py:66
|
||||
msgid "Avatar url"
|
||||
msgstr "头像路径"
|
||||
|
||||
#: users/serializers/user.py:68
|
||||
msgid "Groups name"
|
||||
msgstr "用户组名"
|
||||
|
||||
#: users/serializers/user.py:69
|
||||
msgid "Source name"
|
||||
msgstr "用户来源名"
|
||||
|
||||
#: users/serializers/user.py:71
|
||||
msgid "Super role name"
|
||||
msgstr "超级角色名称"
|
||||
|
||||
#: users/serializers/user.py:72
|
||||
msgid "Total role name"
|
||||
msgstr "汇总角色名称"
|
||||
|
||||
#: users/serializers/user.py:73
|
||||
msgid "MFA enabled"
|
||||
msgstr "是否开启多因子认证"
|
||||
|
||||
#: users/serializers/user.py:74
|
||||
msgid "MFA force enabled"
|
||||
msgstr "强制启用多因子认证"
|
||||
|
||||
#: users/serializers/user.py:97
|
||||
msgid "Role limit to {}"
|
||||
msgstr "角色只能为 {}"
|
||||
|
||||
#: users/serializers/user.py:210
|
||||
msgid "name not unique"
|
||||
msgstr "名称重复"
|
||||
|
||||
|
@ -4891,3 +4887,11 @@ msgstr "旗舰版"
|
|||
#: xpack/plugins/license/models.py:77
|
||||
msgid "Community edition"
|
||||
msgstr "社区版"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The task of self-checking is already running and cannot be started "
|
||||
#~ "repeatedly"
|
||||
#~ msgstr "自检程序已经在运行,不能重复启动"
|
||||
|
||||
#~ msgid "Please wait while your data is being initialized"
|
||||
#~ msgstr "数据正在初始化,请稍等"
|
||||
|
|
|
@ -5,14 +5,17 @@ from django.utils.translation import ugettext as _
|
|||
from rest_framework import status
|
||||
from rest_framework.views import Response
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
from rest_framework.generics import RetrieveAPIView
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from common.permissions import IsSuperUserOrAppUser
|
||||
from common.permissions import IsSuperUserOrAppUser, IsValidUser, UserCanUseCurrentOrg
|
||||
from common.drf.api import JMSBulkRelationModelViewSet
|
||||
from .models import Organization, ROLE
|
||||
from .serializers import (
|
||||
OrgSerializer, OrgReadSerializer,
|
||||
OrgRetrieveSerializer, OrgMemberSerializer,
|
||||
OrgMemberAdminSerializer, OrgMemberUserSerializer
|
||||
OrgMemberAdminSerializer, OrgMemberUserSerializer,
|
||||
CurrentOrgSerializer
|
||||
)
|
||||
from users.models import User, UserGroup
|
||||
from assets.models import Asset, Domain, AdminUser, SystemUser, Label
|
||||
|
@ -129,3 +132,11 @@ class OrgMemberUserRelationBulkViewSet(JMSBulkRelationModelViewSet):
|
|||
objs = list(queryset.all().prefetch_related('user', 'org'))
|
||||
queryset.delete()
|
||||
self.send_m2m_changed_signal(objs, action='post_remove')
|
||||
|
||||
|
||||
class CurrentOrgDetailApi(RetrieveAPIView):
|
||||
serializer_class = CurrentOrgSerializer
|
||||
permission_classes = (IsValidUser, UserCanUseCurrentOrg)
|
||||
|
||||
def get_object(self):
|
||||
return current_org
|
||||
|
|
|
@ -33,12 +33,12 @@ class OrgResourceStatisticsCache(OrgRelatedCache):
|
|||
return self.org
|
||||
|
||||
def compute_users_amount(self):
|
||||
if self.org.is_real():
|
||||
if self.org.is_root():
|
||||
users_amount = User.objects.all().count()
|
||||
else:
|
||||
users_amount = OrganizationMember.objects.values(
|
||||
'user_id'
|
||||
).filter(org_id=self.org.id).distinct().count()
|
||||
else:
|
||||
users_amount = User.objects.all().distinct().count()
|
||||
return users_amount
|
||||
|
||||
def compute_assets_amount(self):
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# Generated by Django 3.1 on 2021-02-19 04:41
|
||||
|
||||
import time
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
default_id = '00000000-0000-0000-0000-000000000001'
|
||||
|
||||
|
||||
def add_default_org(apps, schema_editor):
|
||||
org_cls = apps.get_model('orgs', 'Organization')
|
||||
defaults = {'name': 'DEFAULT', 'id': default_id}
|
||||
org_cls.objects.get_or_create(defaults=defaults, id=default_id)
|
||||
|
||||
|
||||
def migrate_default_org_id(apps, schema_editor):
|
||||
org_app_models = [
|
||||
('applications', ['Application']),
|
||||
('assets', [
|
||||
'AdminUser', 'Asset', 'AuthBook', 'CommandFilter',
|
||||
'CommandFilterRule', 'Domain', 'Gateway', 'GatheredUser',
|
||||
'Label', 'Node', 'SystemUser'
|
||||
]),
|
||||
('audits', ['FTPLog', 'OperateLog']),
|
||||
('ops', ['AdHoc', 'AdHocExecution', 'CommandExecution', 'Task']),
|
||||
('perms', ['ApplicationPermission', 'AssetPermission', 'UserAssetGrantedTreeNodeRelation']),
|
||||
('terminal', ['Session', 'Command']),
|
||||
('tickets', ['Ticket']),
|
||||
('users', ['UserGroup']),
|
||||
('xpack', [
|
||||
'Account', 'SyncInstanceDetail', 'SyncInstanceTask', 'SyncInstanceTaskExecution',
|
||||
'ChangeAuthPlan', 'ChangeAuthPlanExecution', 'ChangeAuthPlanTask',
|
||||
'GatherUserTask', 'GatherUserTaskExecution',
|
||||
]),
|
||||
]
|
||||
print("")
|
||||
for app, models_name in org_app_models:
|
||||
for model_name in models_name:
|
||||
t_start = time.time()
|
||||
print("Migrate model org id: {}".format(model_name), end='')
|
||||
model_cls = apps.get_model(app, model_name)
|
||||
model_cls.objects.filter(org_id='').update(org_id=default_id)
|
||||
interval = round((time.time() - t_start) * 1000, 2)
|
||||
print(" done, use {} ms".format(interval))
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orgs', '0009_auto_20201023_1628'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(add_default_org),
|
||||
migrations.RunPython(migrate_default_org_id)
|
||||
]
|
|
@ -23,14 +23,11 @@ class OrgManager(models.Manager):
|
|||
def all_group_by_org(self):
|
||||
from ..models import Organization
|
||||
orgs = list(Organization.objects.all())
|
||||
orgs.append(Organization.default())
|
||||
querysets = {}
|
||||
for org in orgs:
|
||||
if org.is_real():
|
||||
org_id = org.id
|
||||
else:
|
||||
org_id = ''
|
||||
querysets[org] = super(OrgManager, self).get_queryset().filter(org_id=org_id)
|
||||
org_id = org.id
|
||||
queryset = super(OrgManager, self).get_queryset().filter(org_id=org_id)
|
||||
querysets[org] = queryset
|
||||
return querysets
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -53,12 +50,11 @@ class OrgModelMixin(models.Model):
|
|||
|
||||
def save(self, *args, **kwargs):
|
||||
org = get_current_org()
|
||||
if org is None:
|
||||
return super().save(*args, **kwargs)
|
||||
if org.is_real() or org.is_system():
|
||||
if org.is_root():
|
||||
if not self.org_id:
|
||||
raise ValidationError('Please save in a organization')
|
||||
else:
|
||||
self.org_id = org.id
|
||||
elif org.is_default():
|
||||
self.org_id = ''
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
|
@ -78,10 +74,7 @@ class OrgModelMixin(models.Model):
|
|||
name = self.name
|
||||
elif hasattr(self, 'hostname'):
|
||||
name = self.hostname
|
||||
if self.org.is_real():
|
||||
return name + self.sep + self.org_name
|
||||
else:
|
||||
return name
|
||||
return name + self.sep + self.org_name
|
||||
|
||||
def validate_unique(self, exclude=None):
|
||||
"""
|
||||
|
@ -89,7 +82,7 @@ class OrgModelMixin(models.Model):
|
|||
failed.
|
||||
Form 提交时会使用这个检验
|
||||
"""
|
||||
self.org_id = current_org.id if current_org.is_real() else ''
|
||||
self.org_id = current_org.id
|
||||
if exclude and 'org_id' in exclude:
|
||||
exclude.remove('org_id')
|
||||
unique_checks, date_checks = self._get_unique_checks(exclude=exclude)
|
||||
|
|
|
@ -30,11 +30,9 @@ class Organization(models.Model):
|
|||
orgs = None
|
||||
CACHE_PREFIX = 'JMS_ORG_{}'
|
||||
ROOT_ID = '00000000-0000-0000-0000-000000000000'
|
||||
ROOT_NAME = 'ROOT'
|
||||
DEFAULT_ID = 'DEFAULT'
|
||||
ROOT_NAME = _('GLOBAL')
|
||||
DEFAULT_ID = '00000000-0000-0000-0000-000000000001'
|
||||
DEFAULT_NAME = 'DEFAULT'
|
||||
SYSTEM_ID = '00000000-0000-0000-0000-000000000002'
|
||||
SYSTEM_NAME = 'SYSTEM'
|
||||
_user_admin_orgs = None
|
||||
|
||||
class Meta:
|
||||
|
@ -69,8 +67,6 @@ class Organization(models.Model):
|
|||
return cls.default()
|
||||
elif id_or_name in [cls.ROOT_ID, cls.ROOT_NAME]:
|
||||
return cls.root()
|
||||
elif id_or_name in [cls.SYSTEM_ID, cls.SYSTEM_NAME]:
|
||||
return cls.system()
|
||||
|
||||
try:
|
||||
if is_uuid(id_or_name):
|
||||
|
@ -87,7 +83,7 @@ class Organization(models.Model):
|
|||
|
||||
def get_org_members_by_role(self, role):
|
||||
from users.models import User
|
||||
if self.is_real():
|
||||
if not self.is_root():
|
||||
return self.members.filter(m2m_org_members__role=role)
|
||||
users = User.objects.filter(role=role)
|
||||
return users
|
||||
|
@ -105,20 +101,14 @@ class Organization(models.Model):
|
|||
return self.get_org_members_by_role(ROLE.AUDITOR)
|
||||
|
||||
def org_id(self):
|
||||
if self.is_real():
|
||||
return self.id
|
||||
elif self.is_root():
|
||||
return self.ROOT_ID
|
||||
else:
|
||||
return ''
|
||||
return self.id
|
||||
|
||||
def get_members(self, exclude=()):
|
||||
from users.models import User
|
||||
if self.is_real():
|
||||
members = self.members.exclude(m2m_org_members__role__in=exclude)
|
||||
else:
|
||||
if self.is_root():
|
||||
members = User.objects.exclude(role__in=exclude)
|
||||
|
||||
else:
|
||||
members = self.members.exclude(m2m_org_members__role__in=exclude)
|
||||
return members.exclude(role=User.ROLE.APP).distinct()
|
||||
|
||||
def can_admin_by(self, user):
|
||||
|
@ -129,20 +119,19 @@ class Organization(models.Model):
|
|||
return False
|
||||
|
||||
def can_audit_by(self, user):
|
||||
if user.is_super_auditor:
|
||||
if user.is_superuser or user.is_super_auditor:
|
||||
return True
|
||||
if self.auditors.filter(id=user.id).exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_user_by(self, user):
|
||||
def can_use_by(self, user):
|
||||
if user.is_superuser or user.is_super_auditor:
|
||||
return True
|
||||
if self.users.filter(id=user.id).exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_real(self):
|
||||
return self.id not in (self.DEFAULT_NAME, self.ROOT_ID, self.SYSTEM_ID)
|
||||
|
||||
@classmethod
|
||||
def get_user_orgs_by_role(cls, user, role):
|
||||
if not isinstance(role, (tuple, list)):
|
||||
|
@ -165,7 +154,7 @@ class Organization(models.Model):
|
|||
if user.is_anonymous:
|
||||
return cls.objects.none()
|
||||
if user.is_superuser:
|
||||
return [*cls.objects.all(), cls.default()]
|
||||
return [cls.root(), *cls.objects.all()]
|
||||
return cls.get_user_orgs_by_role(user, ROLE.ADMIN)
|
||||
|
||||
@classmethod
|
||||
|
@ -182,7 +171,7 @@ class Organization(models.Model):
|
|||
if user.is_anonymous:
|
||||
return cls.objects.none()
|
||||
if user.is_super_auditor:
|
||||
return [*cls.objects.all(), cls.default()]
|
||||
return [cls.root(), *cls.objects.all()]
|
||||
return cls.get_user_orgs_by_role(user, ROLE.AUDITOR)
|
||||
|
||||
@classmethod
|
||||
|
@ -190,29 +179,24 @@ class Organization(models.Model):
|
|||
if user.is_anonymous:
|
||||
return cls.objects.none()
|
||||
if user.is_superuser or user.is_super_auditor:
|
||||
return [*cls.objects.all(), cls.default()]
|
||||
return [cls.root(), *cls.objects.all()]
|
||||
return cls.get_user_orgs_by_role(user, (ROLE.AUDITOR, ROLE.ADMIN))
|
||||
|
||||
@classmethod
|
||||
def default(cls):
|
||||
return cls(id=cls.DEFAULT_ID, name=cls.DEFAULT_NAME)
|
||||
defaults = dict(name=cls.DEFAULT_NAME, id=cls.DEFAULT_ID)
|
||||
obj, created = cls.objects.get_or_create(defaults=defaults, id=cls.DEFAULT_ID)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def root(cls):
|
||||
return cls(id=cls.ROOT_ID, name=cls.ROOT_NAME)
|
||||
|
||||
@classmethod
|
||||
def system(cls):
|
||||
return cls(id=cls.SYSTEM_ID, name=cls.SYSTEM_NAME)
|
||||
|
||||
def is_root(self):
|
||||
return self.id is self.ROOT_ID
|
||||
return self.id == self.ROOT_ID
|
||||
|
||||
def is_default(self):
|
||||
return self.id is self.DEFAULT_ID
|
||||
|
||||
def is_system(self):
|
||||
return self.id is self.SYSTEM_ID
|
||||
return str(self.id) == self.DEFAULT_ID
|
||||
|
||||
def change_to(self):
|
||||
from .utils import set_current_org
|
||||
|
@ -282,7 +266,7 @@ class UserRoleMapper(dict):
|
|||
self[ROLE.AUDITOR] = self.auditors
|
||||
|
||||
|
||||
class OrgMemeberManager(models.Manager):
|
||||
class OrgMemberManager(models.Manager):
|
||||
|
||||
def remove_users(self, org, users):
|
||||
from users.models import User
|
||||
|
@ -337,8 +321,11 @@ class OrgMemeberManager(models.Manager):
|
|||
_user = _user.id
|
||||
oms_add.append(self.model(org_id=org.id, user_id=_user, role=_role))
|
||||
|
||||
send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
||||
model=User, pk_set=_users2pks_if_need(users, admins, auditors), using=self.db)
|
||||
pk_set = _users2pks_if_need(users, admins, auditors)
|
||||
send = partial(
|
||||
signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
||||
model=User, pk_set=pk_set, using=self.db
|
||||
)
|
||||
|
||||
send(action='pre_add')
|
||||
self.bulk_create(oms_add, ignore_conflicts=True)
|
||||
|
@ -429,7 +416,7 @@ class OrganizationMember(models.Model):
|
|||
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
|
||||
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
|
||||
|
||||
objects = OrgMemeberManager()
|
||||
objects = OrgMemberManager()
|
||||
|
||||
class Meta:
|
||||
unique_together = [('org', 'user', 'role')]
|
||||
|
|
|
@ -38,7 +38,8 @@ class OrgSerializer(ModelSerializer):
|
|||
list_serializer_class = AdaptedBulkListSerializer
|
||||
fields_mini = ['id', 'name']
|
||||
fields_small = fields_mini + [
|
||||
'created_by', 'date_created', 'comment', 'resource_statistics'
|
||||
'is_default', 'is_root', 'comment',
|
||||
'created_by', 'date_created', 'resource_statistics'
|
||||
]
|
||||
|
||||
fields_m2m = ['users', 'admins', 'auditors']
|
||||
|
@ -127,3 +128,9 @@ class OrgRetrieveSerializer(OrgReadSerializer):
|
|||
|
||||
class Meta(OrgReadSerializer.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class CurrentOrgSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = Organization
|
||||
fields = ['id', 'name', 'is_default', 'is_root', 'comment']
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.urls import re_path
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from django.urls import path
|
||||
from rest_framework_bulk.routes import BulkRouter
|
||||
|
||||
from common import api as capi
|
||||
from .. import api
|
||||
|
||||
|
||||
app_name = 'orgs'
|
||||
router = DefaultRouter()
|
||||
bulk_router = BulkRouter()
|
||||
router = BulkRouter()
|
||||
|
||||
router.register(r'orgs', api.OrgViewSet, 'org')
|
||||
bulk_router.register(r'org-member-relation', api.OrgMemberRelationBulkViewSet, 'org-member-relation')
|
||||
router.register(r'org-member-relation', api.OrgMemberRelationBulkViewSet, 'org-member-relation')
|
||||
|
||||
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/admins',
|
||||
api.OrgMemberAdminRelationBulkViewSet, 'membership-admins')
|
||||
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users',
|
||||
api.OrgMemberUserRelationBulkViewSet, 'membership-users'),
|
||||
|
||||
old_version_urlpatterns = [
|
||||
re_path('(?P<resource>org)/.*', capi.redirect_plural_name_api)
|
||||
|
||||
urlpatterns = [
|
||||
path('orgs/current/', api.CurrentOrgDetailApi.as_view(), name='current-org-detail'),
|
||||
]
|
||||
|
||||
urlpatterns = router.urls + bulk_router.urls + old_version_urlpatterns
|
||||
urlpatterns += router.urls
|
||||
|
|
|
@ -68,12 +68,8 @@ def get_current_org_id():
|
|||
def construct_org_mapper():
|
||||
orgs = Organization.objects.all()
|
||||
org_mapper = {str(org.id): org for org in orgs}
|
||||
default_org = Organization.default()
|
||||
org_mapper.update({
|
||||
'': default_org,
|
||||
Organization.DEFAULT_ID: default_org,
|
||||
Organization.ROOT_ID: Organization.root(),
|
||||
Organization.SYSTEM_ID: Organization.system()
|
||||
})
|
||||
return org_mapper
|
||||
|
||||
|
@ -137,11 +133,9 @@ def get_org_filters():
|
|||
_current_org = get_current_org()
|
||||
if _current_org is None:
|
||||
return kwargs
|
||||
|
||||
if _current_org.is_real():
|
||||
kwargs['org_id'] = _current_org.id
|
||||
elif _current_org.is_default():
|
||||
kwargs["org_id"] = ''
|
||||
if _current_org.is_root():
|
||||
return kwargs
|
||||
kwargs['org_id'] = _current_org.id
|
||||
return kwargs
|
||||
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ class Session(OrgModelMixin):
|
|||
from common.utils.random import random_datetime, random_ip
|
||||
|
||||
org = get_current_org()
|
||||
if not org or not org.is_real():
|
||||
if not org or org.is_root():
|
||||
Organization.default().change_to()
|
||||
i = 0
|
||||
users = User.objects.all()[:100]
|
||||
|
|
|
@ -8,7 +8,7 @@ from orgs.utils import current_org
|
|||
|
||||
class UserQuerysetMixin:
|
||||
def get_queryset(self):
|
||||
if self.request.query_params.get('all') or not current_org.is_real():
|
||||
if self.request.query_params.get('all') or current_org.is_root():
|
||||
queryset = User.objects.exclude(role=User.ROLE.APP)
|
||||
else:
|
||||
queryset = utils.get_current_org_members()
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import uuid
|
||||
|
||||
from rest_framework import generics
|
||||
from common.permissions import IsOrgAdmin
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from django.conf import settings
|
||||
|
||||
|
@ -23,7 +24,7 @@ __all__ = [
|
|||
class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = serializers.UserSerializer
|
||||
permission_classes = (IsAuthenticated,)
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
# Note: we are not updating the user object here.
|
||||
|
@ -37,7 +38,7 @@ class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
|
|||
|
||||
class UserResetPKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||
serializer_class = serializers.UserSerializer
|
||||
permission_classes = (IsAuthenticated,)
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
from ..utils import send_reset_ssh_key_mail
|
||||
|
|
|
@ -48,7 +48,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
|||
queryset = super().get_queryset().prefetch_related(
|
||||
'groups'
|
||||
)
|
||||
if current_org.is_real():
|
||||
if not current_org.is_root():
|
||||
# 为在列表中计算用户在真实组织里的角色
|
||||
queryset = queryset.prefetch_related(
|
||||
Prefetch(
|
||||
|
@ -67,7 +67,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
|||
@staticmethod
|
||||
def set_users_to_org(users, org_roles, update=False):
|
||||
# 只有真实存在的组织才真正关联用户
|
||||
if not current_org or not current_org.is_real():
|
||||
if not current_org or current_org.is_root():
|
||||
return
|
||||
for user, roles in zip(users, org_roles):
|
||||
if update and roles is None:
|
||||
|
@ -94,7 +94,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
|||
return super().get_permissions()
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
if current_org.is_real():
|
||||
if not current_org.is_root():
|
||||
instance.remove()
|
||||
else:
|
||||
return super().perform_destroy(instance)
|
||||
|
@ -150,7 +150,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
|||
data = request.data
|
||||
if not isinstance(data, list):
|
||||
data = [request.data]
|
||||
if not current_org or not current_org.is_real():
|
||||
if not current_org or current_org.is_root():
|
||||
error = {"error": "Not a valid org"}
|
||||
return Response(error, status=400)
|
||||
|
||||
|
|
|
@ -169,20 +169,21 @@ class RoleMixin:
|
|||
def org_roles(self):
|
||||
from orgs.models import ROLE as ORG_ROLE
|
||||
|
||||
if not current_org.is_real():
|
||||
# 不是真实的组织,取 User 本身的角色
|
||||
if current_org.is_root():
|
||||
# root 组织, 取 User 本身的角色
|
||||
if self.is_superuser:
|
||||
return [ORG_ROLE.ADMIN]
|
||||
roles = [ORG_ROLE.ADMIN]
|
||||
elif self.is_super_auditor:
|
||||
roles = [ORG_ROLE.AUDITOR]
|
||||
else:
|
||||
return [ORG_ROLE.USER]
|
||||
|
||||
# 是真实组织,取 OrganizationMember 中的角色
|
||||
roles = [
|
||||
org_member.role
|
||||
for org_member in self.m2m_org_members.all()
|
||||
if org_member.org_id == current_org.id
|
||||
]
|
||||
roles.sort()
|
||||
roles = [ORG_ROLE.USER]
|
||||
else:
|
||||
# 是真实组织, 取 OrganizationMember 中的角色
|
||||
roles = [
|
||||
org_member.role for org_member in self.m2m_org_members.all()
|
||||
if org_member.org_id == current_org.id
|
||||
]
|
||||
roles.sort()
|
||||
return roles
|
||||
|
||||
@lazyproperty
|
||||
|
@ -202,7 +203,7 @@ class RoleMixin:
|
|||
|
||||
def current_org_roles(self):
|
||||
from orgs.models import OrganizationMember, ROLE as ORG_ROLE
|
||||
if not current_org.is_real():
|
||||
if current_org.is_root():
|
||||
if self.is_superuser:
|
||||
return [ORG_ROLE.ADMIN]
|
||||
else:
|
||||
|
@ -297,7 +298,7 @@ class RoleMixin:
|
|||
|
||||
@lazyproperty
|
||||
def can_user_current_org(self):
|
||||
return current_org.can_user_by(self)
|
||||
return current_org.can_use_by(self)
|
||||
|
||||
@lazyproperty
|
||||
def can_admin_or_audit_current_org(self):
|
||||
|
@ -325,7 +326,7 @@ class RoleMixin:
|
|||
return app, access_key
|
||||
|
||||
def remove(self):
|
||||
if not current_org.is_real():
|
||||
if current_org.is_root():
|
||||
return
|
||||
org = Organization.get_instance(current_org.id)
|
||||
OrganizationMember.objects.remove_users(org, [self])
|
||||
|
|
Loading…
Reference in New Issue