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
fit2bot 2021-03-02 14:57:48 +08:00 committed by GitHub
parent 51c9a89b1f
commit a56ac7b34e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 381 additions and 298 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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 * * *')

View File

@ -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.

View File

@ -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是单独部署的一个程序你需要部署lunakoko, </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 "数据正在初始化,请稍等"

View File

@ -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

View File

@ -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):

View File

@ -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)
]

View File

@ -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)

View File

@ -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')]

View File

@ -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']

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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])