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):
|
def get_object(self):
|
||||||
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
|
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
|
||||||
key = self.request.query_params.get("key")
|
key = self.request.query_params.get("key")
|
||||||
|
|
||||||
if not pk and not key:
|
if not pk and not key:
|
||||||
node = Node.org_root()
|
|
||||||
self.is_initial = True
|
self.is_initial = True
|
||||||
|
if current_org.is_root():
|
||||||
|
node = None
|
||||||
|
else:
|
||||||
|
node = Node.org_root()
|
||||||
return node
|
return node
|
||||||
if pk:
|
if pk:
|
||||||
node = get_object_or_404(Node, pk=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)
|
node = get_object_or_404(Node, key=key)
|
||||||
return node
|
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):
|
def get_queryset(self):
|
||||||
query_all = self.request.query_params.get("all", "0") == "all"
|
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:
|
if self.is_initial:
|
||||||
with_self = True
|
with_self = True
|
||||||
else:
|
else:
|
||||||
with_self = False
|
with_self = False
|
||||||
|
|
||||||
|
if not self.instance:
|
||||||
|
return Node.objects.none()
|
||||||
|
|
||||||
if query_all:
|
if query_all:
|
||||||
queryset = self.instance.get_all_children(with_self=with_self)
|
queryset = self.instance.get_all_children(with_self=with_self)
|
||||||
else:
|
else:
|
||||||
|
@ -178,7 +192,7 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
|
||||||
|
|
||||||
def get_assets(self):
|
def get_assets(self):
|
||||||
include_assets = self.request.query_params.get('assets', '0') == '1'
|
include_assets = self.request.query_params.get('assets', '0') == '1'
|
||||||
if not include_assets:
|
if not self.instance or not include_assets:
|
||||||
return []
|
return []
|
||||||
assets = self.instance.get_assets().only(
|
assets = self.instance.get_assets().only(
|
||||||
"id", "hostname", "ip", "os", "platform_id",
|
"id", "hostname", "ip", "os", "platform_id",
|
||||||
|
@ -240,7 +254,10 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
||||||
node.assets.remove(*assets)
|
node.assets.remove(*assets)
|
||||||
|
|
||||||
# 把孤儿资产添加到 root 节点
|
# 把孤儿资产添加到 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)
|
Node.org_root().assets.add(*orphan_assets)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ def compute_parent_key(key):
|
||||||
class NodeQuerySet(models.QuerySet):
|
class NodeQuerySet(models.QuerySet):
|
||||||
def delete(self):
|
def delete(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
#
|
||||||
|
|
||||||
class FamilyMixin:
|
class FamilyMixin:
|
||||||
__parents = None
|
__parents = None
|
||||||
|
@ -446,8 +446,9 @@ class SomeNodesMixin:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_node(cls):
|
def default_node(cls):
|
||||||
with tmp_to_org(Organization.default()):
|
default_org = Organization.default()
|
||||||
defaults = {'value': cls.default_value}
|
with tmp_to_org(default_org):
|
||||||
|
defaults = {'value': default_org.name}
|
||||||
try:
|
try:
|
||||||
obj, created = cls.objects.get_or_create(
|
obj, created = cls.objects.get_or_create(
|
||||||
defaults=defaults, key=cls.default_key,
|
defaults=defaults, key=cls.default_key,
|
||||||
|
@ -482,25 +483,34 @@ class SomeNodesMixin:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_org_root_node(cls):
|
def create_org_root_node(cls):
|
||||||
# 如果使用current_org 在set_current_org时会死循环
|
|
||||||
ori_org = get_current_org()
|
ori_org = get_current_org()
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
if not ori_org.is_real():
|
|
||||||
return cls.default_node()
|
|
||||||
key = cls.get_next_org_root_node_key()
|
key = cls.get_next_org_root_node_key()
|
||||||
root = cls.objects.create(key=key, value=ori_org.name)
|
root = cls.objects.create(key=key, value=ori_org.name)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def org_root(cls):
|
def org_root_nodes(cls):
|
||||||
root = cls.objects.filter(parent_key='')\
|
nodes = cls.objects.filter(parent_key='') \
|
||||||
.filter(key__regex=r'^[0-9]+$')\
|
.filter(key__regex=r'^[0-9]+$') \
|
||||||
.exclude(key__startswith='-')\
|
.exclude(key__startswith='-') \
|
||||||
.order_by('key')
|
.order_by('key')
|
||||||
if root:
|
return nodes
|
||||||
return root[0]
|
|
||||||
|
@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:
|
else:
|
||||||
return cls.create_org_root_node()
|
root = cls.create_org_root_node()
|
||||||
|
return root
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def initial_some_nodes(cls):
|
def initial_some_nodes(cls):
|
||||||
|
@ -519,9 +529,6 @@ class SomeNodesMixin:
|
||||||
if not node_key1:
|
if not node_key1:
|
||||||
logger.info("Not found node that `key` = 1")
|
logger.info("Not found node that `key` = 1")
|
||||||
return
|
return
|
||||||
if not node_key1.org.is_real():
|
|
||||||
logger.info("Org is not real for node that `key` = 1")
|
|
||||||
return
|
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
with tmp_to_org(node_key1.org):
|
with tmp_to_org(node_key1.org):
|
||||||
|
|
|
@ -13,18 +13,20 @@ logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def check_node_assets_amount_task(orgid=None):
|
def check_node_assets_amount_task(org_id=None):
|
||||||
if orgid is None:
|
if org_id is None:
|
||||||
orgs = [*Organization.objects.all(), Organization.default()]
|
orgs = Organization.objects.all()
|
||||||
else:
|
else:
|
||||||
orgs = [Organization.get_instance(orgid)]
|
orgs = [Organization.get_instance(org_id)]
|
||||||
|
|
||||||
for org in orgs:
|
for org in orgs:
|
||||||
try:
|
try:
|
||||||
with tmp_to_org(org):
|
with tmp_to_org(org):
|
||||||
check_node_assets_amount()
|
check_node_assets_amount()
|
||||||
except AcquireFailed:
|
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 * * *')
|
@register_as_period_task(crontab='0 2 * * *')
|
||||||
|
|
|
@ -110,12 +110,17 @@ class PermissionsMixin(UserPassesTestMixin):
|
||||||
return True
|
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):
|
def has_permission(self, request, view):
|
||||||
return request.user.can_update_password()
|
return request.user.can_update_password()
|
||||||
|
|
||||||
|
|
||||||
class UserCanUpdateSSHKey:
|
class UserCanUpdateSSHKey(permissions.BasePermission):
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
return request.user.can_update_ssh_key()
|
return request.user.can_update_ssh_key()
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||||
|
@ -32,8 +32,8 @@ msgstr "远程应用"
|
||||||
msgid "Custom"
|
msgid "Custom"
|
||||||
msgstr "自定义"
|
msgstr "自定义"
|
||||||
|
|
||||||
#: applications/models/application.py:10 assets/models/asset.py:149
|
#: applications/models/application.py:10 assets/models/asset.py:142
|
||||||
#: assets/models/base.py:234 assets/models/cluster.py:18
|
#: assets/models/base.py:235 assets/models/cluster.py:18
|
||||||
#: assets/models/cmd_filter.py:21 assets/models/domain.py:21
|
#: 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
|
#: 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
|
#: orgs/models.py:23 perms/models/base.py:48 settings/models.py:29
|
||||||
|
@ -78,7 +78,7 @@ msgstr "类别"
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "类型"
|
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
|
#: assets/models/domain.py:27 assets/models/domain.py:55
|
||||||
msgid "Domain"
|
msgid "Domain"
|
||||||
msgstr "网域"
|
msgstr "网域"
|
||||||
|
@ -89,8 +89,8 @@ msgstr ""
|
||||||
|
|
||||||
# msgid "Date created"
|
# msgid "Date created"
|
||||||
# msgstr "创建日期"
|
# msgstr "创建日期"
|
||||||
#: applications/models/application.py:23 assets/models/asset.py:154
|
#: applications/models/application.py:23 assets/models/asset.py:147
|
||||||
#: assets/models/asset.py:230 assets/models/base.py:239
|
#: assets/models/asset.py:223 assets/models/base.py:240
|
||||||
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:23
|
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:23
|
||||||
#: assets/models/cmd_filter.py:57 assets/models/domain.py:22
|
#: assets/models/cmd_filter.py:57 assets/models/domain.py:22
|
||||||
#: assets/models/domain.py:56 assets/models/group.py:23
|
#: 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/mysql_workbench.py:22
|
||||||
#: applications/serializers/attrs/application_type/oracle.py:11
|
#: applications/serializers/attrs/application_type/oracle.py:11
|
||||||
#: applications/serializers/attrs/application_type/pgsql.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"
|
msgid "Port"
|
||||||
msgstr "端口"
|
msgstr "端口"
|
||||||
|
|
||||||
#: applications/serializers/attrs/application_category/remote_app.py:33
|
#: 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/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/asset_user.py:47 assets/serializers/asset_user.py:84
|
||||||
#: assets/serializers/system_user.py:191 audits/models.py:38
|
#: 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/models.py:19
|
||||||
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:39
|
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:39
|
||||||
#: users/templates/users/user_asset_permission.html:40
|
#: 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/custom.py:21
|
||||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:30
|
#: applications/serializers/attrs/application_type/mysql_workbench.py:30
|
||||||
#: applications/serializers/attrs/application_type/vmware_client.py:26
|
#: 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
|
#: audits/models.py:99 authentication/forms.py:11
|
||||||
#: authentication/templates/authentication/login.html:101
|
#: authentication/templates/authentication/login.html:101
|
||||||
#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:515
|
#: 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/custom.py:25
|
||||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:34
|
#: applications/serializers/attrs/application_type/mysql_workbench.py:34
|
||||||
#: applications/serializers/attrs/application_type/vmware_client.py:30
|
#: 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
|
#: audits/signals_handler.py:42 authentication/forms.py:13
|
||||||
#: authentication/templates/authentication/login.html:109
|
#: authentication/templates/authentication/login.html:109
|
||||||
#: settings/serializers/settings.py:84 users/forms/user.py:22
|
#: settings/serializers/settings.py:84 users/forms/user.py:22
|
||||||
|
@ -204,7 +204,7 @@ msgid "Target url"
|
||||||
msgstr "目标URL"
|
msgstr "目标URL"
|
||||||
|
|
||||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:18
|
#: 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
|
#: assets/serializers/asset_user.py:46 settings/serializers/settings.py:103
|
||||||
#: users/templates/users/_granted_assets.html:26
|
#: users/templates/users/_granted_assets.html:26
|
||||||
#: users/templates/users/user_asset_permission.html:156
|
#: users/templates/users/user_asset_permission.html:156
|
||||||
|
@ -219,15 +219,15 @@ msgstr "删除失败,存在关联资产"
|
||||||
msgid "Number required"
|
msgid "Number required"
|
||||||
msgstr "需要为数字"
|
msgstr "需要为数字"
|
||||||
|
|
||||||
#: assets/api/node.py:67
|
#: assets/api/node.py:60
|
||||||
msgid "You can't update the root node name"
|
msgid "You can't update the root node name"
|
||||||
msgstr "不能修改根节点名称"
|
msgstr "不能修改根节点名称"
|
||||||
|
|
||||||
#: assets/api/node.py:74
|
#: assets/api/node.py:67
|
||||||
msgid "You can't delete the root node ({})"
|
msgid "You can't delete the root node ({})"
|
||||||
msgstr "不能删除根节点 ({})"
|
msgstr "不能删除根节点 ({})"
|
||||||
|
|
||||||
#: assets/api/node.py:77
|
#: assets/api/node.py:70
|
||||||
msgid "Deletion failed and the node contains children or assets"
|
msgid "Deletion failed and the node contains children or assets"
|
||||||
msgstr "删除失败,节点包含子节点或资产"
|
msgstr "删除失败,节点包含子节点或资产"
|
||||||
|
|
||||||
|
@ -239,137 +239,137 @@ msgstr "不能移除资产的管理用户账号"
|
||||||
msgid "Latest version could not be delete"
|
msgid "Latest version could not be delete"
|
||||||
msgstr "最新版本的不能被删除"
|
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"
|
msgid "Base"
|
||||||
msgstr "基础"
|
msgstr "基础"
|
||||||
|
|
||||||
#: assets/models/asset.py:151
|
#: assets/models/asset.py:144
|
||||||
msgid "Charset"
|
msgid "Charset"
|
||||||
msgstr "编码"
|
msgstr "编码"
|
||||||
|
|
||||||
#: assets/models/asset.py:152 tickets/models/ticket.py:40
|
#: assets/models/asset.py:145 tickets/models/ticket.py:40
|
||||||
msgid "Meta"
|
msgid "Meta"
|
||||||
msgstr "元数据"
|
msgstr "元数据"
|
||||||
|
|
||||||
#: assets/models/asset.py:153
|
#: assets/models/asset.py:146
|
||||||
msgid "Internal"
|
msgid "Internal"
|
||||||
msgstr "内部的"
|
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
|
#: assets/serializers/asset.py:66
|
||||||
msgid "Platform"
|
msgid "Platform"
|
||||||
msgstr "系统平台"
|
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
|
#: assets/serializers/gathered_user.py:20 settings/serializers/settings.py:102
|
||||||
#: users/templates/users/_granted_assets.html:25
|
#: users/templates/users/_granted_assets.html:25
|
||||||
#: users/templates/users/user_asset_permission.html:157
|
#: users/templates/users/user_asset_permission.html:157
|
||||||
msgid "Hostname"
|
msgid "Hostname"
|
||||||
msgstr "主机名"
|
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
|
#: assets/models/user.py:120 terminal/serializers/session.py:29
|
||||||
#: terminal/serializers/storage.py:69
|
#: terminal/serializers/storage.py:69
|
||||||
msgid "Protocol"
|
msgid "Protocol"
|
||||||
msgstr "协议"
|
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
|
#: perms/serializers/asset/user_permission.py:41
|
||||||
msgid "Protocols"
|
msgid "Protocols"
|
||||||
msgstr "协议组"
|
msgstr "协议组"
|
||||||
|
|
||||||
#: assets/models/asset.py:199 assets/models/user.py:115
|
#: assets/models/asset.py:192 assets/models/user.py:115
|
||||||
#: perms/models/asset_permission.py:97
|
#: perms/models/asset_permission.py:100
|
||||||
#: xpack/plugins/change_auth_plan/models.py:56
|
#: xpack/plugins/change_auth_plan/models.py:56
|
||||||
#: xpack/plugins/gathered_user/models.py:24
|
#: xpack/plugins/gathered_user/models.py:24
|
||||||
msgid "Nodes"
|
msgid "Nodes"
|
||||||
msgstr "节点"
|
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
|
#: assets/models/domain.py:57 assets/models/label.py:22
|
||||||
#: authentication/models.py:46
|
#: authentication/models.py:46
|
||||||
msgid "Is active"
|
msgid "Is active"
|
||||||
msgstr "激活"
|
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
|
#: assets/models/user.py:66 templates/_nav.html:44
|
||||||
#: xpack/plugins/cloud/models.py:143 xpack/plugins/cloud/serializers.py:137
|
#: xpack/plugins/cloud/models.py:143 xpack/plugins/cloud/serializers.py:137
|
||||||
msgid "Admin user"
|
msgid "Admin user"
|
||||||
msgstr "管理用户"
|
msgstr "管理用户"
|
||||||
|
|
||||||
#: assets/models/asset.py:206
|
#: assets/models/asset.py:199
|
||||||
msgid "Public IP"
|
msgid "Public IP"
|
||||||
msgstr "公网IP"
|
msgstr "公网IP"
|
||||||
|
|
||||||
#: assets/models/asset.py:207
|
#: assets/models/asset.py:200
|
||||||
msgid "Asset number"
|
msgid "Asset number"
|
||||||
msgstr "资产编号"
|
msgstr "资产编号"
|
||||||
|
|
||||||
#: assets/models/asset.py:210
|
#: assets/models/asset.py:203
|
||||||
msgid "Vendor"
|
msgid "Vendor"
|
||||||
msgstr "制造商"
|
msgstr "制造商"
|
||||||
|
|
||||||
#: assets/models/asset.py:211
|
#: assets/models/asset.py:204
|
||||||
msgid "Model"
|
msgid "Model"
|
||||||
msgstr "型号"
|
msgstr "型号"
|
||||||
|
|
||||||
#: assets/models/asset.py:212
|
#: assets/models/asset.py:205
|
||||||
msgid "Serial number"
|
msgid "Serial number"
|
||||||
msgstr "序列号"
|
msgstr "序列号"
|
||||||
|
|
||||||
#: assets/models/asset.py:214
|
#: assets/models/asset.py:207
|
||||||
msgid "CPU model"
|
msgid "CPU model"
|
||||||
msgstr "CPU型号"
|
msgstr "CPU型号"
|
||||||
|
|
||||||
#: assets/models/asset.py:215
|
#: assets/models/asset.py:208
|
||||||
msgid "CPU count"
|
msgid "CPU count"
|
||||||
msgstr "CPU数量"
|
msgstr "CPU数量"
|
||||||
|
|
||||||
#: assets/models/asset.py:216
|
#: assets/models/asset.py:209
|
||||||
msgid "CPU cores"
|
msgid "CPU cores"
|
||||||
msgstr "CPU核数"
|
msgstr "CPU核数"
|
||||||
|
|
||||||
#: assets/models/asset.py:217
|
#: assets/models/asset.py:210
|
||||||
msgid "CPU vcpus"
|
msgid "CPU vcpus"
|
||||||
msgstr "CPU总数"
|
msgstr "CPU总数"
|
||||||
|
|
||||||
#: assets/models/asset.py:218
|
#: assets/models/asset.py:211
|
||||||
msgid "Memory"
|
msgid "Memory"
|
||||||
msgstr "内存"
|
msgstr "内存"
|
||||||
|
|
||||||
#: assets/models/asset.py:219
|
#: assets/models/asset.py:212
|
||||||
msgid "Disk total"
|
msgid "Disk total"
|
||||||
msgstr "硬盘大小"
|
msgstr "硬盘大小"
|
||||||
|
|
||||||
#: assets/models/asset.py:220
|
#: assets/models/asset.py:213
|
||||||
msgid "Disk info"
|
msgid "Disk info"
|
||||||
msgstr "硬盘信息"
|
msgstr "硬盘信息"
|
||||||
|
|
||||||
#: assets/models/asset.py:222
|
#: assets/models/asset.py:215
|
||||||
msgid "OS"
|
msgid "OS"
|
||||||
msgstr "操作系统"
|
msgstr "操作系统"
|
||||||
|
|
||||||
#: assets/models/asset.py:223
|
#: assets/models/asset.py:216
|
||||||
msgid "OS version"
|
msgid "OS version"
|
||||||
msgstr "系统版本"
|
msgstr "系统版本"
|
||||||
|
|
||||||
#: assets/models/asset.py:224
|
#: assets/models/asset.py:217
|
||||||
msgid "OS arch"
|
msgid "OS arch"
|
||||||
msgstr "系统架构"
|
msgstr "系统架构"
|
||||||
|
|
||||||
#: assets/models/asset.py:225
|
#: assets/models/asset.py:218
|
||||||
msgid "Hostname raw"
|
msgid "Hostname raw"
|
||||||
msgstr "主机名原始"
|
msgstr "主机名原始"
|
||||||
|
|
||||||
#: assets/models/asset.py:227 templates/_nav.html:46
|
#: assets/models/asset.py:220 templates/_nav.html:46
|
||||||
msgid "Labels"
|
msgid "Labels"
|
||||||
msgstr "标签管理"
|
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/cluster.py:28 assets/models/cmd_filter.py:26
|
||||||
#: assets/models/cmd_filter.py:60 assets/models/group.py:21
|
#: 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
|
#: 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
|
#: 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/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
|
#: xpack/plugins/cloud/models.py:156 xpack/plugins/gathered_user/models.py:30
|
||||||
|
@ -378,12 +378,12 @@ msgstr "创建者"
|
||||||
|
|
||||||
# msgid "Created by"
|
# msgid "Created by"
|
||||||
# msgstr "创建者"
|
# 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/cluster.py:26 assets/models/domain.py:24
|
||||||
#: assets/models/gathered_user.py:19 assets/models/group.py:22
|
#: 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
|
#: 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
|
#: 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
|
#: users/templates/users/user_group_detail.html:58
|
||||||
#: xpack/plugins/cloud/models.py:61 xpack/plugins/cloud/models.py:159
|
#: xpack/plugins/cloud/models.py:61 xpack/plugins/cloud/models.py:159
|
||||||
msgid "Date created"
|
msgid "Date created"
|
||||||
|
@ -405,21 +405,21 @@ msgstr "版本"
|
||||||
msgid "AuthBook"
|
msgid "AuthBook"
|
||||||
msgstr ""
|
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:197
|
||||||
#: xpack/plugins/change_auth_plan/models.py:292
|
#: xpack/plugins/change_auth_plan/models.py:292
|
||||||
msgid "SSH private key"
|
msgid "SSH private key"
|
||||||
msgstr "SSH密钥"
|
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:193
|
||||||
#: xpack/plugins/change_auth_plan/models.py:288
|
#: xpack/plugins/change_auth_plan/models.py:288
|
||||||
msgid "SSH public key"
|
msgid "SSH public key"
|
||||||
msgstr "SSH公钥"
|
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
|
#: 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"
|
msgid "Date updated"
|
||||||
msgstr "更新日期"
|
msgstr "更新日期"
|
||||||
|
|
||||||
|
@ -556,9 +556,9 @@ msgstr "默认资产组"
|
||||||
|
|
||||||
#: assets/models/label.py:15 audits/models.py:36 audits/models.py:56
|
#: 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
|
#: audits/models.py:69 audits/serializers.py:81 authentication/models.py:44
|
||||||
#: authentication/models.py:95 orgs/models.py:18 orgs/models.py:423
|
#: authentication/models.py:95 orgs/models.py:18 orgs/models.py:408
|
||||||
#: perms/models/asset_permission.py:173 perms/models/base.py:49
|
#: perms/models/base.py:49 templates/index.html:78
|
||||||
#: templates/index.html:78 terminal/backends/command/models.py:18
|
#: terminal/backends/command/models.py:18
|
||||||
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:37
|
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:37
|
||||||
#: tickets/models/comment.py:17 users/forms/group.py:15
|
#: tickets/models/comment.py:17 users/forms/group.py:15
|
||||||
#: users/models/user.py:158 users/models/user.py:665
|
#: users/models/user.py:158 users/models/user.py:665
|
||||||
|
@ -575,31 +575,31 @@ msgstr "默认资产组"
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr "用户"
|
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"
|
msgid "Value"
|
||||||
msgstr "值"
|
msgstr "值"
|
||||||
|
|
||||||
#: assets/models/node.py:143
|
#: assets/models/node.py:152
|
||||||
msgid "New node"
|
msgid "New node"
|
||||||
msgstr "新节点"
|
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"
|
msgid "empty"
|
||||||
msgstr "空"
|
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"
|
msgid "Key"
|
||||||
msgstr "键"
|
msgstr "键"
|
||||||
|
|
||||||
#: assets/models/node.py:414
|
#: assets/models/node.py:546
|
||||||
msgid "Full value"
|
msgid "Full value"
|
||||||
msgstr "全称"
|
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"
|
msgid "Parent key"
|
||||||
msgstr "ssh私钥"
|
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:41
|
||||||
#: users/templates/users/user_asset_permission.html:73
|
#: users/templates/users/user_asset_permission.html:73
|
||||||
#: users/templates/users/user_asset_permission.html:158
|
#: users/templates/users/user_asset_permission.html:158
|
||||||
|
@ -668,7 +668,7 @@ msgstr "用户组"
|
||||||
|
|
||||||
#: assets/models/user.py:221 audits/models.py:39
|
#: assets/models/user.py:221 audits/models.py:39
|
||||||
#: perms/models/application_permission.py:31
|
#: 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/models.py:20
|
||||||
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:41
|
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:41
|
||||||
#: users/templates/users/_granted_assets.html:27
|
#: users/templates/users/_granted_assets.html:27
|
||||||
|
@ -727,7 +727,7 @@ msgstr "硬件信息"
|
||||||
msgid "Org name"
|
msgid "Org name"
|
||||||
msgstr "组织名称"
|
msgstr "组织名称"
|
||||||
|
|
||||||
#: assets/serializers/asset.py:162 assets/serializers/asset.py:201
|
#: assets/serializers/asset.py:162 assets/serializers/asset.py:194
|
||||||
msgid "Connectivity"
|
msgid "Connectivity"
|
||||||
msgstr "连接"
|
msgstr "连接"
|
||||||
|
|
||||||
|
@ -873,11 +873,6 @@ msgstr "更新节点资产硬件信息: {}"
|
||||||
msgid "Gather assets users"
|
msgid "Gather assets users"
|
||||||
msgstr "收集资产上的用户"
|
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/push_system_user.py:184
|
||||||
#: assets/tasks/system_user_connectivity.py:89
|
#: assets/tasks/system_user_connectivity.py:89
|
||||||
msgid "System user is dynamic: {}"
|
msgid "System user is dynamic: {}"
|
||||||
|
@ -1081,7 +1076,7 @@ msgstr "用户代理"
|
||||||
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
|
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
|
||||||
#: authentication/templates/authentication/login_otp.html:6
|
#: authentication/templates/authentication/login_otp.html:6
|
||||||
#: users/forms/profile.py:52 users/models/user.py:539
|
#: 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
|
#: users/templates/users/user_profile.html:87
|
||||||
msgid "MFA"
|
msgid "MFA"
|
||||||
msgstr "多因子认证"
|
msgstr "多因子认证"
|
||||||
|
@ -1355,7 +1350,7 @@ msgid "Show"
|
||||||
msgstr "显示"
|
msgstr "显示"
|
||||||
|
|
||||||
#: authentication/templates/authentication/_access_key_modal.html:66
|
#: 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:94
|
||||||
#: users/templates/users/user_profile.html:163
|
#: users/templates/users/user_profile.html:163
|
||||||
#: users/templates/users/user_profile.html:166
|
#: users/templates/users/user_profile.html:166
|
||||||
|
@ -1364,7 +1359,7 @@ msgid "Disable"
|
||||||
msgstr "禁用"
|
msgstr "禁用"
|
||||||
|
|
||||||
#: authentication/templates/authentication/_access_key_modal.html:67
|
#: 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:92
|
||||||
#: users/templates/users/user_profile.html:170
|
#: users/templates/users/user_profile.html:170
|
||||||
msgid "Enable"
|
msgid "Enable"
|
||||||
|
@ -1605,7 +1600,7 @@ msgstr "不能包含特殊字符"
|
||||||
msgid "<h1>Flow service unavailable, check it</h1>"
|
msgid "<h1>Flow service unavailable, check it</h1>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: jumpserver/views/other.py:26
|
#: jumpserver/views/other.py:27
|
||||||
msgid ""
|
msgid ""
|
||||||
"<div>Luna is a separately deployed program, you need to deploy Luna, koko, "
|
"<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, "
|
"configure nginx for url distribution,</div> </div>If you see this page, "
|
||||||
|
@ -1614,11 +1609,11 @@ msgstr ""
|
||||||
"<div>Luna是单独部署的一个程序,你需要部署luna,koko, </div><div>如果你看到了"
|
"<div>Luna是单独部署的一个程序,你需要部署luna,koko, </div><div>如果你看到了"
|
||||||
"这个页面,证明你访问的不是nginx监听的端口,祝你好运</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"
|
msgid "Websocket server run on port: {}, you should proxy it on nginx"
|
||||||
msgstr "Websocket 服务运行在端口: {}, 请检查nginx是否代理是否设置"
|
msgstr "Websocket 服务运行在端口: {}, 请检查nginx是否代理是否设置"
|
||||||
|
|
||||||
#: jumpserver/views/other.py:91
|
#: jumpserver/views/other.py:92
|
||||||
msgid ""
|
msgid ""
|
||||||
"<div>Koko is a separately deployed program, you need to deploy Koko, "
|
"<div>Koko is a separately deployed program, you need to deploy Koko, "
|
||||||
"configure nginx for url distribution,</div> </div>If you see this page, "
|
"configure nginx for url distribution,</div> </div>If you see this page, "
|
||||||
|
@ -1795,8 +1790,8 @@ msgstr "组织包含未删除的资源"
|
||||||
msgid "The current organization cannot be deleted"
|
msgid "The current organization cannot be deleted"
|
||||||
msgstr "当前组织不能被删除"
|
msgstr "当前组织不能被删除"
|
||||||
|
|
||||||
#: orgs/mixins/models.py:56 orgs/mixins/serializers.py:25 orgs/models.py:41
|
#: orgs/mixins/models.py:53 orgs/mixins/serializers.py:25 orgs/models.py:39
|
||||||
#: orgs/models.py:422 orgs/serializers.py:100
|
#: orgs/models.py:407 orgs/serializers.py:100
|
||||||
#: tickets/serializers/ticket/ticket.py:81
|
#: tickets/serializers/ticket/ticket.py:81
|
||||||
msgid "Organization"
|
msgid "Organization"
|
||||||
msgstr "组织"
|
msgstr "组织"
|
||||||
|
@ -1809,7 +1804,11 @@ msgstr "组织管理员"
|
||||||
msgid "Organization auditor"
|
msgid "Organization auditor"
|
||||||
msgstr "组织审计员"
|
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/_select_user_modal.html:15
|
||||||
#: users/templates/users/user_detail.html:73
|
#: users/templates/users/user_detail.html:73
|
||||||
#: users/templates/users/user_list.html:16
|
#: users/templates/users/user_list.html:16
|
||||||
|
@ -1817,7 +1816,7 @@ msgstr "组织审计员"
|
||||||
msgid "Role"
|
msgid "Role"
|
||||||
msgstr "角色"
|
msgstr "角色"
|
||||||
|
|
||||||
#: perms/const.py:7 perms/utils/asset/user_permission.py:28
|
#: perms/const.py:7 perms/models/asset_permission.py:189
|
||||||
msgid "Ungrouped"
|
msgid "Ungrouped"
|
||||||
msgstr "未分组"
|
msgstr "未分组"
|
||||||
|
|
||||||
|
@ -1841,47 +1840,52 @@ msgstr "应用程序"
|
||||||
msgid "Application permission"
|
msgid "Application permission"
|
||||||
msgstr "应用管理"
|
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"
|
msgid "All"
|
||||||
msgstr "全部"
|
msgstr "全部"
|
||||||
|
|
||||||
#: perms/models/asset_permission.py:35
|
#: perms/models/asset_permission.py:38
|
||||||
msgid "Connect"
|
msgid "Connect"
|
||||||
msgstr "连接"
|
msgstr "连接"
|
||||||
|
|
||||||
#: perms/models/asset_permission.py:36
|
#: perms/models/asset_permission.py:39
|
||||||
msgid "Upload file"
|
msgid "Upload file"
|
||||||
msgstr "上传文件"
|
msgstr "上传文件"
|
||||||
|
|
||||||
#: perms/models/asset_permission.py:37
|
#: perms/models/asset_permission.py:40
|
||||||
msgid "Download file"
|
msgid "Download file"
|
||||||
msgstr "下载文件"
|
msgstr "下载文件"
|
||||||
|
|
||||||
#: perms/models/asset_permission.py:38
|
#: perms/models/asset_permission.py:41
|
||||||
msgid "Upload download"
|
msgid "Upload download"
|
||||||
msgstr "上传下载"
|
msgstr "上传下载"
|
||||||
|
|
||||||
#: perms/models/asset_permission.py:39
|
#: perms/models/asset_permission.py:42
|
||||||
msgid "Clipboard copy"
|
msgid "Clipboard copy"
|
||||||
msgstr "剪贴板复制"
|
msgstr "剪贴板复制"
|
||||||
|
|
||||||
#: perms/models/asset_permission.py:40
|
#: perms/models/asset_permission.py:43
|
||||||
msgid "Clipboard paste"
|
msgid "Clipboard paste"
|
||||||
msgstr "剪贴板粘贴"
|
msgstr "剪贴板粘贴"
|
||||||
|
|
||||||
#: perms/models/asset_permission.py:41
|
#: perms/models/asset_permission.py:44
|
||||||
msgid "Clipboard copy paste"
|
msgid "Clipboard copy paste"
|
||||||
msgstr "剪贴板复制粘贴"
|
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"
|
msgid "Actions"
|
||||||
msgstr "动作"
|
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
|
#: users/templates/users/_user_detail_nav_header.html:31
|
||||||
msgid "Asset permission"
|
msgid "Asset permission"
|
||||||
msgstr "资产授权"
|
msgstr "资产授权"
|
||||||
|
|
||||||
|
#: perms/models/asset_permission.py:191
|
||||||
|
msgid "Favorite"
|
||||||
|
msgstr "收藏夹"
|
||||||
|
|
||||||
#: perms/models/base.py:50 templates/_nav.html:21 users/forms/user.py:168
|
#: 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/models/group.py:31 users/models/user.py:523
|
||||||
#: users/templates/users/_select_user_modal.html:16
|
#: users/templates/users/_select_user_modal.html:16
|
||||||
|
@ -1912,11 +1916,11 @@ msgid ""
|
||||||
"permission type. ({})"
|
"permission type. ({})"
|
||||||
msgstr "应用列表中包含与授权类型不同的应用。({})"
|
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"
|
msgid "Is expired"
|
||||||
msgstr "是否过期"
|
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"
|
msgid "Is valid"
|
||||||
msgstr "账户是否有效"
|
msgstr "账户是否有效"
|
||||||
|
|
||||||
|
@ -1932,14 +1936,6 @@ msgstr "用户组数量"
|
||||||
msgid "System users amount"
|
msgid "System users amount"
|
||||||
msgstr "系统用户数量"
|
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
|
#: settings/api/common.py:24
|
||||||
msgid "Test mail sent to {}, please check"
|
msgid "Test mail sent to {}, please check"
|
||||||
msgstr "邮件已经发送{}, 请检查"
|
msgstr "邮件已经发送{}, 请检查"
|
||||||
|
@ -3054,7 +3050,7 @@ msgstr "索引"
|
||||||
msgid "Doc type"
|
msgid "Doc type"
|
||||||
msgstr "文档类型"
|
msgstr "文档类型"
|
||||||
|
|
||||||
#: terminal/serializers/terminal.py:44 terminal/serializers/terminal.py:52
|
#: terminal/serializers/terminal.py:47 terminal/serializers/terminal.py:55
|
||||||
msgid "Not found"
|
msgid "Not found"
|
||||||
msgstr "没有发现"
|
msgstr "没有发现"
|
||||||
|
|
||||||
|
@ -3530,8 +3526,8 @@ msgid "Public key should not be the same as your old one."
|
||||||
msgstr "不能和原来的密钥相同"
|
msgstr "不能和原来的密钥相同"
|
||||||
|
|
||||||
#: users/forms/profile.py:137 users/forms/user.py:90
|
#: users/forms/profile.py:137 users/forms/user.py:90
|
||||||
#: users/serializers/user.py:192 users/serializers/user.py:277
|
#: users/serializers/profile.py:74 users/serializers/profile.py:147
|
||||||
#: users/serializers/user.py:335
|
#: users/serializers/profile.py:160
|
||||||
msgid "Not a valid ssh public key"
|
msgid "Not a valid ssh public key"
|
||||||
msgstr "SSH密钥不合法"
|
msgstr "SSH密钥不合法"
|
||||||
|
|
||||||
|
@ -3555,15 +3551,15 @@ msgstr "添加到用户组"
|
||||||
msgid "* Your password does not meet the requirements"
|
msgid "* Your password does not meet the requirements"
|
||||||
msgstr "* 您的密码不符合要求"
|
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"
|
msgid "Reset link will be generated and sent to the user"
|
||||||
msgstr "生成重置密码链接,通过邮件发送给用户"
|
msgstr "生成重置密码链接,通过邮件发送给用户"
|
||||||
|
|
||||||
#: users/forms/user.py:125 users/serializers/user.py:36
|
#: users/forms/user.py:125 users/serializers/user.py:21
|
||||||
msgid "Set password"
|
msgid "Set password"
|
||||||
msgstr "设置密码"
|
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/models.py:61
|
||||||
#: xpack/plugins/change_auth_plan/serializers.py:30
|
#: xpack/plugins/change_auth_plan/serializers.py:30
|
||||||
msgid "Password strategy"
|
msgid "Password strategy"
|
||||||
|
@ -3605,75 +3601,75 @@ msgstr "管理员"
|
||||||
msgid "Administrator is the super user of system"
|
msgid "Administrator is the super user of system"
|
||||||
msgstr "Administrator是初始的超级管理员"
|
msgstr "Administrator是初始的超级管理员"
|
||||||
|
|
||||||
#: users/serializers/user.py:45
|
#: users/serializers/profile.py:32
|
||||||
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
|
|
||||||
msgid "The old password is incorrect"
|
msgid "The old password is incorrect"
|
||||||
msgstr "旧密码错误"
|
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"
|
msgid "The newly set password is inconsistent"
|
||||||
msgstr "两次密码不一致"
|
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"
|
msgid "name not unique"
|
||||||
msgstr "名称重复"
|
msgstr "名称重复"
|
||||||
|
|
||||||
|
@ -4891,3 +4887,11 @@ msgstr "旗舰版"
|
||||||
#: xpack/plugins/license/models.py:77
|
#: xpack/plugins/license/models.py:77
|
||||||
msgid "Community edition"
|
msgid "Community edition"
|
||||||
msgstr "社区版"
|
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 import status
|
||||||
from rest_framework.views import Response
|
from rest_framework.views import Response
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
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 common.drf.api import JMSBulkRelationModelViewSet
|
||||||
from .models import Organization, ROLE
|
from .models import Organization, ROLE
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
OrgSerializer, OrgReadSerializer,
|
OrgSerializer, OrgReadSerializer,
|
||||||
OrgRetrieveSerializer, OrgMemberSerializer,
|
OrgRetrieveSerializer, OrgMemberSerializer,
|
||||||
OrgMemberAdminSerializer, OrgMemberUserSerializer
|
OrgMemberAdminSerializer, OrgMemberUserSerializer,
|
||||||
|
CurrentOrgSerializer
|
||||||
)
|
)
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
from assets.models import Asset, Domain, AdminUser, SystemUser, Label
|
from assets.models import Asset, Domain, AdminUser, SystemUser, Label
|
||||||
|
@ -129,3 +132,11 @@ class OrgMemberUserRelationBulkViewSet(JMSBulkRelationModelViewSet):
|
||||||
objs = list(queryset.all().prefetch_related('user', 'org'))
|
objs = list(queryset.all().prefetch_related('user', 'org'))
|
||||||
queryset.delete()
|
queryset.delete()
|
||||||
self.send_m2m_changed_signal(objs, action='post_remove')
|
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
|
return self.org
|
||||||
|
|
||||||
def compute_users_amount(self):
|
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(
|
users_amount = OrganizationMember.objects.values(
|
||||||
'user_id'
|
'user_id'
|
||||||
).filter(org_id=self.org.id).distinct().count()
|
).filter(org_id=self.org.id).distinct().count()
|
||||||
else:
|
|
||||||
users_amount = User.objects.all().distinct().count()
|
|
||||||
return users_amount
|
return users_amount
|
||||||
|
|
||||||
def compute_assets_amount(self):
|
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):
|
def all_group_by_org(self):
|
||||||
from ..models import Organization
|
from ..models import Organization
|
||||||
orgs = list(Organization.objects.all())
|
orgs = list(Organization.objects.all())
|
||||||
orgs.append(Organization.default())
|
|
||||||
querysets = {}
|
querysets = {}
|
||||||
for org in orgs:
|
for org in orgs:
|
||||||
if org.is_real():
|
org_id = org.id
|
||||||
org_id = org.id
|
queryset = super(OrgManager, self).get_queryset().filter(org_id=org_id)
|
||||||
else:
|
querysets[org] = queryset
|
||||||
org_id = ''
|
|
||||||
querysets[org] = super(OrgManager, self).get_queryset().filter(org_id=org_id)
|
|
||||||
return querysets
|
return querysets
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -53,12 +50,11 @@ class OrgModelMixin(models.Model):
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
org = get_current_org()
|
org = get_current_org()
|
||||||
if org is None:
|
if org.is_root():
|
||||||
return super().save(*args, **kwargs)
|
if not self.org_id:
|
||||||
if org.is_real() or org.is_system():
|
raise ValidationError('Please save in a organization')
|
||||||
|
else:
|
||||||
self.org_id = org.id
|
self.org_id = org.id
|
||||||
elif org.is_default():
|
|
||||||
self.org_id = ''
|
|
||||||
return super().save(*args, **kwargs)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -78,10 +74,7 @@ class OrgModelMixin(models.Model):
|
||||||
name = self.name
|
name = self.name
|
||||||
elif hasattr(self, 'hostname'):
|
elif hasattr(self, 'hostname'):
|
||||||
name = self.hostname
|
name = self.hostname
|
||||||
if self.org.is_real():
|
return name + self.sep + self.org_name
|
||||||
return name + self.sep + self.org_name
|
|
||||||
else:
|
|
||||||
return name
|
|
||||||
|
|
||||||
def validate_unique(self, exclude=None):
|
def validate_unique(self, exclude=None):
|
||||||
"""
|
"""
|
||||||
|
@ -89,7 +82,7 @@ class OrgModelMixin(models.Model):
|
||||||
failed.
|
failed.
|
||||||
Form 提交时会使用这个检验
|
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:
|
if exclude and 'org_id' in exclude:
|
||||||
exclude.remove('org_id')
|
exclude.remove('org_id')
|
||||||
unique_checks, date_checks = self._get_unique_checks(exclude=exclude)
|
unique_checks, date_checks = self._get_unique_checks(exclude=exclude)
|
||||||
|
|
|
@ -30,11 +30,9 @@ class Organization(models.Model):
|
||||||
orgs = None
|
orgs = None
|
||||||
CACHE_PREFIX = 'JMS_ORG_{}'
|
CACHE_PREFIX = 'JMS_ORG_{}'
|
||||||
ROOT_ID = '00000000-0000-0000-0000-000000000000'
|
ROOT_ID = '00000000-0000-0000-0000-000000000000'
|
||||||
ROOT_NAME = 'ROOT'
|
ROOT_NAME = _('GLOBAL')
|
||||||
DEFAULT_ID = 'DEFAULT'
|
DEFAULT_ID = '00000000-0000-0000-0000-000000000001'
|
||||||
DEFAULT_NAME = 'DEFAULT'
|
DEFAULT_NAME = 'DEFAULT'
|
||||||
SYSTEM_ID = '00000000-0000-0000-0000-000000000002'
|
|
||||||
SYSTEM_NAME = 'SYSTEM'
|
|
||||||
_user_admin_orgs = None
|
_user_admin_orgs = None
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -69,8 +67,6 @@ class Organization(models.Model):
|
||||||
return cls.default()
|
return cls.default()
|
||||||
elif id_or_name in [cls.ROOT_ID, cls.ROOT_NAME]:
|
elif id_or_name in [cls.ROOT_ID, cls.ROOT_NAME]:
|
||||||
return cls.root()
|
return cls.root()
|
||||||
elif id_or_name in [cls.SYSTEM_ID, cls.SYSTEM_NAME]:
|
|
||||||
return cls.system()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if is_uuid(id_or_name):
|
if is_uuid(id_or_name):
|
||||||
|
@ -87,7 +83,7 @@ class Organization(models.Model):
|
||||||
|
|
||||||
def get_org_members_by_role(self, role):
|
def get_org_members_by_role(self, role):
|
||||||
from users.models import User
|
from users.models import User
|
||||||
if self.is_real():
|
if not self.is_root():
|
||||||
return self.members.filter(m2m_org_members__role=role)
|
return self.members.filter(m2m_org_members__role=role)
|
||||||
users = User.objects.filter(role=role)
|
users = User.objects.filter(role=role)
|
||||||
return users
|
return users
|
||||||
|
@ -105,20 +101,14 @@ class Organization(models.Model):
|
||||||
return self.get_org_members_by_role(ROLE.AUDITOR)
|
return self.get_org_members_by_role(ROLE.AUDITOR)
|
||||||
|
|
||||||
def org_id(self):
|
def org_id(self):
|
||||||
if self.is_real():
|
return self.id
|
||||||
return self.id
|
|
||||||
elif self.is_root():
|
|
||||||
return self.ROOT_ID
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def get_members(self, exclude=()):
|
def get_members(self, exclude=()):
|
||||||
from users.models import User
|
from users.models import User
|
||||||
if self.is_real():
|
if self.is_root():
|
||||||
members = self.members.exclude(m2m_org_members__role__in=exclude)
|
|
||||||
else:
|
|
||||||
members = User.objects.exclude(role__in=exclude)
|
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()
|
return members.exclude(role=User.ROLE.APP).distinct()
|
||||||
|
|
||||||
def can_admin_by(self, user):
|
def can_admin_by(self, user):
|
||||||
|
@ -129,20 +119,19 @@ class Organization(models.Model):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def can_audit_by(self, user):
|
def can_audit_by(self, user):
|
||||||
if user.is_super_auditor:
|
if user.is_superuser or user.is_super_auditor:
|
||||||
return True
|
return True
|
||||||
if self.auditors.filter(id=user.id).exists():
|
if self.auditors.filter(id=user.id).exists():
|
||||||
return True
|
return True
|
||||||
return False
|
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():
|
if self.users.filter(id=user.id).exists():
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_real(self):
|
|
||||||
return self.id not in (self.DEFAULT_NAME, self.ROOT_ID, self.SYSTEM_ID)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_user_orgs_by_role(cls, user, role):
|
def get_user_orgs_by_role(cls, user, role):
|
||||||
if not isinstance(role, (tuple, list)):
|
if not isinstance(role, (tuple, list)):
|
||||||
|
@ -165,7 +154,7 @@ class Organization(models.Model):
|
||||||
if user.is_anonymous:
|
if user.is_anonymous:
|
||||||
return cls.objects.none()
|
return cls.objects.none()
|
||||||
if user.is_superuser:
|
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)
|
return cls.get_user_orgs_by_role(user, ROLE.ADMIN)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -182,7 +171,7 @@ class Organization(models.Model):
|
||||||
if user.is_anonymous:
|
if user.is_anonymous:
|
||||||
return cls.objects.none()
|
return cls.objects.none()
|
||||||
if user.is_super_auditor:
|
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)
|
return cls.get_user_orgs_by_role(user, ROLE.AUDITOR)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -190,29 +179,24 @@ class Organization(models.Model):
|
||||||
if user.is_anonymous:
|
if user.is_anonymous:
|
||||||
return cls.objects.none()
|
return cls.objects.none()
|
||||||
if user.is_superuser or user.is_super_auditor:
|
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))
|
return cls.get_user_orgs_by_role(user, (ROLE.AUDITOR, ROLE.ADMIN))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default(cls):
|
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
|
@classmethod
|
||||||
def root(cls):
|
def root(cls):
|
||||||
return cls(id=cls.ROOT_ID, name=cls.ROOT_NAME)
|
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):
|
def is_root(self):
|
||||||
return self.id is self.ROOT_ID
|
return self.id == self.ROOT_ID
|
||||||
|
|
||||||
def is_default(self):
|
def is_default(self):
|
||||||
return self.id is self.DEFAULT_ID
|
return str(self.id) == self.DEFAULT_ID
|
||||||
|
|
||||||
def is_system(self):
|
|
||||||
return self.id is self.SYSTEM_ID
|
|
||||||
|
|
||||||
def change_to(self):
|
def change_to(self):
|
||||||
from .utils import set_current_org
|
from .utils import set_current_org
|
||||||
|
@ -282,7 +266,7 @@ class UserRoleMapper(dict):
|
||||||
self[ROLE.AUDITOR] = self.auditors
|
self[ROLE.AUDITOR] = self.auditors
|
||||||
|
|
||||||
|
|
||||||
class OrgMemeberManager(models.Manager):
|
class OrgMemberManager(models.Manager):
|
||||||
|
|
||||||
def remove_users(self, org, users):
|
def remove_users(self, org, users):
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
@ -337,8 +321,11 @@ class OrgMemeberManager(models.Manager):
|
||||||
_user = _user.id
|
_user = _user.id
|
||||||
oms_add.append(self.model(org_id=org.id, user_id=_user, role=_role))
|
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,
|
pk_set = _users2pks_if_need(users, admins, auditors)
|
||||||
model=User, pk_set=_users2pks_if_need(users, admins, auditors), using=self.db)
|
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')
|
send(action='pre_add')
|
||||||
self.bulk_create(oms_add, ignore_conflicts=True)
|
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"))
|
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
|
||||||
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
|
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
|
||||||
|
|
||||||
objects = OrgMemeberManager()
|
objects = OrgMemberManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [('org', 'user', 'role')]
|
unique_together = [('org', 'user', 'role')]
|
||||||
|
|
|
@ -38,7 +38,8 @@ class OrgSerializer(ModelSerializer):
|
||||||
list_serializer_class = AdaptedBulkListSerializer
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
fields_mini = ['id', 'name']
|
fields_mini = ['id', 'name']
|
||||||
fields_small = fields_mini + [
|
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']
|
fields_m2m = ['users', 'admins', 'auditors']
|
||||||
|
@ -127,3 +128,9 @@ class OrgRetrieveSerializer(OrgReadSerializer):
|
||||||
|
|
||||||
class Meta(OrgReadSerializer.Meta):
|
class Meta(OrgReadSerializer.Meta):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CurrentOrgSerializer(ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Organization
|
||||||
|
fields = ['id', 'name', 'is_default', 'is_root', 'comment']
|
||||||
|
|
|
@ -1,28 +1,26 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.urls import re_path
|
from django.urls import path
|
||||||
from rest_framework.routers import DefaultRouter
|
|
||||||
from rest_framework_bulk.routes import BulkRouter
|
from rest_framework_bulk.routes import BulkRouter
|
||||||
|
|
||||||
from common import api as capi
|
|
||||||
from .. import api
|
from .. import api
|
||||||
|
|
||||||
|
|
||||||
app_name = 'orgs'
|
app_name = 'orgs'
|
||||||
router = DefaultRouter()
|
router = BulkRouter()
|
||||||
bulk_router = BulkRouter()
|
|
||||||
|
|
||||||
router.register(r'orgs', api.OrgViewSet, 'org')
|
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',
|
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/admins',
|
||||||
api.OrgMemberAdminRelationBulkViewSet, 'membership-admins')
|
api.OrgMemberAdminRelationBulkViewSet, 'membership-admins')
|
||||||
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users',
|
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users',
|
||||||
api.OrgMemberUserRelationBulkViewSet, '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():
|
def construct_org_mapper():
|
||||||
orgs = Organization.objects.all()
|
orgs = Organization.objects.all()
|
||||||
org_mapper = {str(org.id): org for org in orgs}
|
org_mapper = {str(org.id): org for org in orgs}
|
||||||
default_org = Organization.default()
|
|
||||||
org_mapper.update({
|
org_mapper.update({
|
||||||
'': default_org,
|
|
||||||
Organization.DEFAULT_ID: default_org,
|
|
||||||
Organization.ROOT_ID: Organization.root(),
|
Organization.ROOT_ID: Organization.root(),
|
||||||
Organization.SYSTEM_ID: Organization.system()
|
|
||||||
})
|
})
|
||||||
return org_mapper
|
return org_mapper
|
||||||
|
|
||||||
|
@ -137,11 +133,9 @@ def get_org_filters():
|
||||||
_current_org = get_current_org()
|
_current_org = get_current_org()
|
||||||
if _current_org is None:
|
if _current_org is None:
|
||||||
return kwargs
|
return kwargs
|
||||||
|
if _current_org.is_root():
|
||||||
if _current_org.is_real():
|
return kwargs
|
||||||
kwargs['org_id'] = _current_org.id
|
kwargs['org_id'] = _current_org.id
|
||||||
elif _current_org.is_default():
|
|
||||||
kwargs["org_id"] = ''
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ class Session(OrgModelMixin):
|
||||||
from common.utils.random import random_datetime, random_ip
|
from common.utils.random import random_datetime, random_ip
|
||||||
|
|
||||||
org = get_current_org()
|
org = get_current_org()
|
||||||
if not org or not org.is_real():
|
if not org or org.is_root():
|
||||||
Organization.default().change_to()
|
Organization.default().change_to()
|
||||||
i = 0
|
i = 0
|
||||||
users = User.objects.all()[:100]
|
users = User.objects.all()[:100]
|
||||||
|
|
|
@ -8,7 +8,7 @@ from orgs.utils import current_org
|
||||||
|
|
||||||
class UserQuerysetMixin:
|
class UserQuerysetMixin:
|
||||||
def get_queryset(self):
|
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)
|
queryset = User.objects.exclude(role=User.ROLE.APP)
|
||||||
else:
|
else:
|
||||||
queryset = utils.get_current_org_members()
|
queryset = utils.get_current_org_members()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
|
from common.permissions import IsOrgAdmin
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ __all__ = [
|
||||||
class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
|
class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = serializers.UserSerializer
|
||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
# Note: we are not updating the user object here.
|
# Note: we are not updating the user object here.
|
||||||
|
@ -37,7 +38,7 @@ class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||||
|
|
||||||
class UserResetPKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
class UserResetPKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = serializers.UserSerializer
|
||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
from ..utils import send_reset_ssh_key_mail
|
from ..utils import send_reset_ssh_key_mail
|
||||||
|
|
|
@ -48,7 +48,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
||||||
queryset = super().get_queryset().prefetch_related(
|
queryset = super().get_queryset().prefetch_related(
|
||||||
'groups'
|
'groups'
|
||||||
)
|
)
|
||||||
if current_org.is_real():
|
if not current_org.is_root():
|
||||||
# 为在列表中计算用户在真实组织里的角色
|
# 为在列表中计算用户在真实组织里的角色
|
||||||
queryset = queryset.prefetch_related(
|
queryset = queryset.prefetch_related(
|
||||||
Prefetch(
|
Prefetch(
|
||||||
|
@ -67,7 +67,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_users_to_org(users, org_roles, update=False):
|
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
|
return
|
||||||
for user, roles in zip(users, org_roles):
|
for user, roles in zip(users, org_roles):
|
||||||
if update and roles is None:
|
if update and roles is None:
|
||||||
|
@ -94,7 +94,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
def perform_destroy(self, instance):
|
def perform_destroy(self, instance):
|
||||||
if current_org.is_real():
|
if not current_org.is_root():
|
||||||
instance.remove()
|
instance.remove()
|
||||||
else:
|
else:
|
||||||
return super().perform_destroy(instance)
|
return super().perform_destroy(instance)
|
||||||
|
@ -150,7 +150,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
||||||
data = request.data
|
data = request.data
|
||||||
if not isinstance(data, list):
|
if not isinstance(data, list):
|
||||||
data = [request.data]
|
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"}
|
error = {"error": "Not a valid org"}
|
||||||
return Response(error, status=400)
|
return Response(error, status=400)
|
||||||
|
|
||||||
|
|
|
@ -169,20 +169,21 @@ class RoleMixin:
|
||||||
def org_roles(self):
|
def org_roles(self):
|
||||||
from orgs.models import ROLE as ORG_ROLE
|
from orgs.models import ROLE as ORG_ROLE
|
||||||
|
|
||||||
if not current_org.is_real():
|
if current_org.is_root():
|
||||||
# 不是真实的组织,取 User 本身的角色
|
# root 组织, 取 User 本身的角色
|
||||||
if self.is_superuser:
|
if self.is_superuser:
|
||||||
return [ORG_ROLE.ADMIN]
|
roles = [ORG_ROLE.ADMIN]
|
||||||
|
elif self.is_super_auditor:
|
||||||
|
roles = [ORG_ROLE.AUDITOR]
|
||||||
else:
|
else:
|
||||||
return [ORG_ROLE.USER]
|
roles = [ORG_ROLE.USER]
|
||||||
|
else:
|
||||||
# 是真实组织,取 OrganizationMember 中的角色
|
# 是真实组织, 取 OrganizationMember 中的角色
|
||||||
roles = [
|
roles = [
|
||||||
org_member.role
|
org_member.role for org_member in self.m2m_org_members.all()
|
||||||
for org_member in self.m2m_org_members.all()
|
if org_member.org_id == current_org.id
|
||||||
if org_member.org_id == current_org.id
|
]
|
||||||
]
|
roles.sort()
|
||||||
roles.sort()
|
|
||||||
return roles
|
return roles
|
||||||
|
|
||||||
@lazyproperty
|
@lazyproperty
|
||||||
|
@ -202,7 +203,7 @@ class RoleMixin:
|
||||||
|
|
||||||
def current_org_roles(self):
|
def current_org_roles(self):
|
||||||
from orgs.models import OrganizationMember, ROLE as ORG_ROLE
|
from orgs.models import OrganizationMember, ROLE as ORG_ROLE
|
||||||
if not current_org.is_real():
|
if current_org.is_root():
|
||||||
if self.is_superuser:
|
if self.is_superuser:
|
||||||
return [ORG_ROLE.ADMIN]
|
return [ORG_ROLE.ADMIN]
|
||||||
else:
|
else:
|
||||||
|
@ -297,7 +298,7 @@ class RoleMixin:
|
||||||
|
|
||||||
@lazyproperty
|
@lazyproperty
|
||||||
def can_user_current_org(self):
|
def can_user_current_org(self):
|
||||||
return current_org.can_user_by(self)
|
return current_org.can_use_by(self)
|
||||||
|
|
||||||
@lazyproperty
|
@lazyproperty
|
||||||
def can_admin_or_audit_current_org(self):
|
def can_admin_or_audit_current_org(self):
|
||||||
|
@ -325,7 +326,7 @@ class RoleMixin:
|
||||||
return app, access_key
|
return app, access_key
|
||||||
|
|
||||||
def remove(self):
|
def remove(self):
|
||||||
if not current_org.is_real():
|
if current_org.is_root():
|
||||||
return
|
return
|
||||||
org = Organization.get_instance(current_org.id)
|
org = Organization.get_instance(current_org.id)
|
||||||
OrganizationMember.objects.remove_users(org, [self])
|
OrganizationMember.objects.remove_users(org, [self])
|
||||||
|
|
Loading…
Reference in New Issue