From af85d551ad99258d0c65e3c1c47d11c872039990 Mon Sep 17 00:00:00 2001 From: xinwen Date: Fri, 14 Aug 2020 17:09:23 +0800 Subject: [PATCH 01/14] =?UTF-8?q?fix(users):=20=E4=BF=AE=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=A7=92=E8=89=B2=E6=98=BE=E7=A4=BA=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | Bin 56654 -> 56727 bytes apps/locale/zh/LC_MESSAGES/django.po | 153 +++++++++--------- .../migrations/0029_auto_20200814_1650.py | 18 +++ apps/users/models/user.py | 4 +- apps/users/serializers/user.py | 7 +- 5 files changed, 105 insertions(+), 77 deletions(-) create mode 100644 apps/users/migrations/0029_auto_20200814_1650.py diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 27d124dbad066e7c581997b82e21ae13c5acb293..1c1c347551b3c911cf5ad7a019cd1313f60a8cbe 100644 GIT binary patch delta 16596 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 ZMYqxy`Qte&ZcN&EGh^JkHisWa{vQv=In)3E delta 16531 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\n" "Language-Team: JumpServer team\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: applications/const.py:52 +#: applications/const.py:53 msgid "Custom" msgstr "自定义" @@ -27,8 +27,8 @@ msgstr "自定义" #: assets/models/cmd_filter.py:21 assets/models/domain.py:20 #: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24 #: orgs/models.py:22 perms/models/base.py:48 settings/models.py:27 -#: terminal/models.py:26 terminal/models.py:342 terminal/models.py:374 -#: terminal/models.py:411 users/forms/profile.py:20 users/models/group.py:15 +#: terminal/models.py:27 terminal/models.py:344 terminal/models.py:376 +#: terminal/models.py:413 users/forms/profile.py:20 users/models/group.py:15 #: users/models/user.py:489 users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_asset_permission.html:37 #: users/templates/users/user_asset_permission.html:154 @@ -47,7 +47,7 @@ msgid "Name" msgstr "名称" #: applications/models/database_app.py:22 applications/models/k8s_app.py:14 -#: assets/models/cmd_filter.py:52 terminal/models.py:376 terminal/models.py:413 +#: assets/models/cmd_filter.py:52 terminal/models.py:378 terminal/models.py:415 #: tickets/models/ticket.py:40 #: users/templates/users/user_granted_database_app.html:35 msgid "Type" @@ -77,8 +77,8 @@ msgstr "数据库" #: assets/models/cmd_filter.py:57 assets/models/domain.py:21 #: assets/models/domain.py:54 assets/models/group.py:23 #: assets/models/label.py:23 ops/models/adhoc.py:37 orgs/models.py:25 -#: perms/models/base.py:56 settings/models.py:32 terminal/models.py:36 -#: terminal/models.py:381 terminal/models.py:418 users/models/group.py:16 +#: perms/models/base.py:56 settings/models.py:32 terminal/models.py:37 +#: terminal/models.py:383 terminal/models.py:420 users/models/group.py:16 #: users/models/user.py:522 users/templates/users/user_detail.html:115 #: users/templates/users/user_granted_database_app.html:38 #: users/templates/users/user_granted_remote_app.html:37 @@ -116,11 +116,11 @@ msgstr "Kubernetes应用" #: applications/models/remote_app.py:23 assets/models/asset.py:352 #: 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:44 -#: assets/serializers/system_user.py:176 audits/models.py:38 +#: assets/serializers/asset_user.py:84 assets/serializers/system_user.py:46 +#: assets/serializers/system_user.py:179 audits/models.py:38 #: perms/forms/asset_permission.py:89 perms/models/asset_permission.py:90 #: templates/index.html:82 terminal/backends/command/models.py:19 -#: terminal/backends/command/serializers.py:13 terminal/models.py:187 +#: terminal/backends/command/serializers.py:13 terminal/models.py:188 #: users/templates/users/user_asset_permission.html:40 #: users/templates/users/user_asset_permission.html:70 #: users/templates/users/user_granted_remote_app.html:36 @@ -233,7 +233,7 @@ msgid "Hostname" msgstr "主机名" #: assets/models/asset.py:190 assets/models/domain.py:52 -#: assets/models/user.py:116 terminal/serializers/session.py:29 +#: assets/models/user.py:117 terminal/serializers/session.py:29 msgid "Protocol" msgstr "协议" @@ -247,7 +247,7 @@ msgstr "协议组" msgid "Domain" msgstr "网域" -#: assets/models/asset.py:195 assets/models/user.py:111 +#: assets/models/asset.py:195 assets/models/user.py:112 #: perms/models/asset_permission.py:91 #: xpack/plugins/change_auth_plan/models.py:56 #: xpack/plugins/gathered_user/models.py:24 @@ -261,7 +261,7 @@ msgid "Is active" msgstr "激活" #: assets/models/asset.py:199 assets/models/cluster.py:19 -#: assets/models/user.py:65 templates/_nav.html:44 +#: assets/models/user.py:66 templates/_nav.html:44 #: xpack/plugins/cloud/models.py:133 xpack/plugins/cloud/serializers.py:83 msgid "Admin user" msgstr "管理用户" @@ -453,7 +453,7 @@ msgstr "北京电信" msgid "BGP full netcom" msgstr "BGP全网通" -#: assets/models/cmd_filter.py:33 assets/models/user.py:121 +#: assets/models/cmd_filter.py:33 assets/models/user.py:122 msgid "Command filter" msgstr "命令过滤器" @@ -462,7 +462,7 @@ msgid "Regex" msgstr "正则表达式" #: assets/models/cmd_filter.py:41 ops/models/command.py:23 -#: terminal/backends/command/serializers.py:15 terminal/models.py:196 +#: terminal/backends/command/serializers.py:15 terminal/models.py:197 msgid "Command" msgstr "命令" @@ -478,7 +478,7 @@ msgstr "允许" msgid "Filter" msgstr "过滤器" -#: assets/models/cmd_filter.py:53 assets/models/user.py:115 +#: assets/models/cmd_filter.py:53 assets/models/user.py:116 msgid "Priority" msgstr "优先级" @@ -551,7 +551,7 @@ msgstr "默认资产组" #: perms/forms/asset_permission.py:83 perms/forms/database_app_permission.py:38 #: perms/forms/remote_app_permission.py:40 perms/models/base.py:49 #: templates/index.html:78 terminal/backends/command/models.py:18 -#: terminal/backends/command/serializers.py:12 terminal/models.py:185 +#: terminal/backends/command/serializers.py:12 terminal/models.py:186 #: tickets/models/ticket.py:30 tickets/models/ticket.py:137 #: tickets/serializers/request_asset_perm.py:65 #: tickets/serializers/ticket.py:31 users/forms/group.py:15 @@ -597,8 +597,8 @@ msgstr "收藏夹" msgid "Key" msgstr "键" -#: assets/models/node.py:511 assets/serializers/system_user.py:43 -#: assets/serializers/system_user.py:175 perms/forms/asset_permission.py:92 +#: assets/models/node.py:511 assets/serializers/system_user.py:45 +#: assets/serializers/system_user.py:178 perms/forms/asset_permission.py:92 #: perms/forms/asset_permission.py:99 #: users/templates/users/user_asset_permission.html:41 #: users/templates/users/user_asset_permission.html:73 @@ -607,65 +607,73 @@ msgstr "键" msgid "Node" msgstr "节点" -#: assets/models/user.py:107 +#: assets/models/user.py:108 msgid "Automatic login" msgstr "自动登录" -#: assets/models/user.py:108 +#: assets/models/user.py:109 msgid "Manually login" msgstr "手动登录" -#: assets/models/user.py:110 +#: assets/models/user.py:111 msgid "Username same with user" msgstr "用户名与用户相同" -#: assets/models/user.py:112 templates/_nav.html:39 +#: assets/models/user.py:113 templates/_nav.html:39 #: xpack/plugins/change_auth_plan/models.py:52 msgid "Assets" msgstr "资产管理" -#: assets/models/user.py:113 templates/_nav.html:17 +#: assets/models/user.py:114 templates/_nav.html:17 #: users/views/profile/password.py:42 users/views/profile/pubkey.py:36 msgid "Users" msgstr "用户管理" -#: assets/models/user.py:114 users/templates/users/user_group_list.html:90 +#: assets/models/user.py:115 users/templates/users/user_group_list.html:90 #: users/templates/users/user_profile.html:124 msgid "User groups" msgstr "用户组" -#: assets/models/user.py:117 +#: assets/models/user.py:118 msgid "Auto push" msgstr "自动推送" -#: assets/models/user.py:118 +#: assets/models/user.py:119 msgid "Sudo" msgstr "Sudo" -#: assets/models/user.py:119 +#: assets/models/user.py:120 msgid "Shell" msgstr "Shell" -#: assets/models/user.py:120 +#: assets/models/user.py:121 msgid "Login mode" msgstr "登录模式" -#: assets/models/user.py:122 +#: assets/models/user.py:123 msgid "SFTP Root" msgstr "SFTP根路径" -#: assets/models/user.py:123 authentication/models.py:88 +#: assets/models/user.py:124 authentication/models.py:88 msgid "Token" msgstr "" -#: assets/models/user.py:198 audits/models.py:39 +#: assets/models/user.py:125 +msgid "Home" +msgstr "家目录" + +#: assets/models/user.py:126 +msgid "System groups" +msgstr "用户组" + +#: assets/models/user.py:201 audits/models.py:39 #: perms/forms/asset_permission.py:95 perms/forms/remote_app_permission.py:49 #: perms/models/asset_permission.py:92 #: perms/models/database_app_permission.py:22 #: perms/models/k8s_app_permission.py:22 #: perms/models/remote_app_permission.py:16 templates/_nav.html:45 #: terminal/backends/command/models.py:20 -#: terminal/backends/command/serializers.py:14 terminal/models.py:189 +#: terminal/backends/command/serializers.py:14 terminal/models.py:190 #: tickets/serializers/request_asset_perm.py:27 #: users/templates/users/_granted_assets.html:27 #: users/templates/users/user_asset_permission.html:42 @@ -753,23 +761,23 @@ msgstr "值" msgid "The same level node name cannot be the same" msgstr "同级别节点名字不能重复" -#: assets/serializers/system_user.py:45 assets/serializers/system_user.py:177 +#: assets/serializers/system_user.py:47 assets/serializers/system_user.py:180 msgid "Login mode display" msgstr "登录模式显示" -#: assets/serializers/system_user.py:85 +#: assets/serializers/system_user.py:87 msgid "Username same with user with protocol {} only allow 1" msgstr "用户名和用户相同的一种协议只允许存在一个" -#: assets/serializers/system_user.py:98 +#: assets/serializers/system_user.py:100 msgid "* Automatic login mode must fill in the username." msgstr "自动登录模式,必须填写用户名" -#: assets/serializers/system_user.py:106 +#: assets/serializers/system_user.py:108 msgid "Path should starts with /" msgstr "路径应该以 / 开头" -#: assets/serializers/system_user.py:117 +#: assets/serializers/system_user.py:119 msgid "Password or private key required" msgstr "密码或密钥密码需要一个" @@ -821,25 +829,25 @@ msgstr "更新节点资产硬件信息: {}" msgid "Gather assets users" msgstr "收集资产上的用户" -#: assets/tasks/push_system_user.py:148 +#: assets/tasks/push_system_user.py:176 #: assets/tasks/system_user_connectivity.py:89 msgid "System user is dynamic: {}" msgstr "系统用户是动态的: {}" -#: assets/tasks/push_system_user.py:179 +#: assets/tasks/push_system_user.py:207 msgid "Start push system user for platform: [{}]" msgstr "推送系统用户到平台: [{}]" -#: assets/tasks/push_system_user.py:180 +#: assets/tasks/push_system_user.py:208 #: assets/tasks/system_user_connectivity.py:81 msgid "Hosts count: {}" msgstr "主机数量: {}" -#: assets/tasks/push_system_user.py:197 assets/tasks/push_system_user.py:213 +#: assets/tasks/push_system_user.py:225 assets/tasks/push_system_user.py:241 msgid "Push system users to assets: {}" msgstr "推送系统用户到入资产: {}" -#: assets/tasks/push_system_user.py:205 +#: assets/tasks/push_system_user.py:233 msgid "Push system users to asset: {}({}) => {}" msgstr "推送系统用户到入资产: {}({}) => {}" @@ -919,7 +927,7 @@ msgid "Symlink" msgstr "建立软链接" #: audits/models.py:37 audits/models.py:60 audits/models.py:71 -#: terminal/models.py:192 +#: terminal/models.py:193 msgid "Remote addr" msgstr "远端地址" @@ -937,7 +945,7 @@ msgid "Success" msgstr "成功" #: audits/models.py:43 ops/models/command.py:28 perms/models/base.py:52 -#: terminal/models.py:199 tickets/serializers/request_asset_perm.py:29 +#: terminal/models.py:200 tickets/serializers/request_asset_perm.py:29 #: xpack/plugins/change_auth_plan/models.py:177 #: xpack/plugins/change_auth_plan/models.py:308 #: xpack/plugins/gathered_user/models.py:76 @@ -1191,7 +1199,7 @@ msgstr "登录复核 {}" msgid "SSO auth closed" msgstr "SSO 认证关闭了" -#: authentication/errors.py:218 authentication/views/login.py:237 +#: authentication/errors.py:218 authentication/views/login.py:243 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" @@ -1374,11 +1382,11 @@ msgstr "复制成功" msgid "Welcome back, please enter username and password to login" msgstr "欢迎回来,请输入用户名和密码登录" -#: authentication/views/login.py:83 +#: authentication/views/login.py:84 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: authentication/views/login.py:183 +#: authentication/views/login.py:189 msgid "" "Wait for {} confirm, You also can copy link to her/him
\n" " Don't close this page" @@ -1386,19 +1394,19 @@ msgstr "" "等待 {} 确认, 你也可以复制链接发给他/她
\n" " 不要关闭本页面" -#: authentication/views/login.py:188 +#: authentication/views/login.py:194 msgid "No ticket found" msgstr "没有发现工单" -#: authentication/views/login.py:220 +#: authentication/views/login.py:226 msgid "Logout success" msgstr "退出登录成功" -#: authentication/views/login.py:221 +#: authentication/views/login.py:227 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: authentication/views/login.py:236 +#: authentication/views/login.py:242 msgid "Please change your password" msgstr "请修改密码" @@ -2469,63 +2477,63 @@ msgstr "风险等级" msgid "Bulk create not support" msgstr "不支持批量创建" -#: terminal/models.py:27 +#: terminal/models.py:28 msgid "Remote Address" msgstr "远端地址" -#: terminal/models.py:28 +#: terminal/models.py:29 msgid "SSH Port" msgstr "SSH端口" -#: terminal/models.py:29 +#: terminal/models.py:30 msgid "HTTP Port" msgstr "HTTP端口" -#: terminal/models.py:30 +#: terminal/models.py:31 msgid "Command storage" msgstr "命令存储" -#: terminal/models.py:31 +#: terminal/models.py:32 msgid "Replay storage" msgstr "录像存储" -#: terminal/models.py:154 +#: terminal/models.py:155 msgid "Session Online" msgstr "在线会话" -#: terminal/models.py:155 +#: terminal/models.py:156 msgid "CPU Usage" msgstr "CPU使用" -#: terminal/models.py:156 +#: terminal/models.py:157 msgid "Memory Used" msgstr "内存使用" -#: terminal/models.py:157 +#: terminal/models.py:158 msgid "Connections" msgstr "连接数" -#: terminal/models.py:158 +#: terminal/models.py:159 msgid "Threads" msgstr "线程数" -#: terminal/models.py:159 +#: terminal/models.py:160 msgid "Boot Time" msgstr "运行时间" -#: terminal/models.py:191 +#: terminal/models.py:192 msgid "Login from" msgstr "登录来源" -#: terminal/models.py:195 +#: terminal/models.py:196 msgid "Replay" msgstr "回放" -#: terminal/models.py:200 +#: terminal/models.py:201 msgid "Date end" msgstr "结束日期" -#: terminal/models.py:343 +#: terminal/models.py:345 msgid "Args" msgstr "参数" @@ -2863,12 +2871,12 @@ msgid "Password strategy" msgstr "密码策略" #: users/models/user.py:156 -msgid "Super administrator" -msgstr "超级管理员" +msgid "System administrator" +msgstr "系统管理员" #: users/models/user.py:158 -msgid "Super auditor" -msgstr "超级审计员" +msgid "System auditor" +msgstr "系统审计员" #: users/models/user.py:159 msgid "Application" @@ -4489,9 +4497,6 @@ msgstr "旗舰版" #~ msgid "Have existed: " #~ msgstr "已经存在: " -#~ msgid "Home" -#~ msgstr "家目录" - #~ msgid "Uid" #~ msgstr "Uid" diff --git a/apps/users/migrations/0029_auto_20200814_1650.py b/apps/users/migrations/0029_auto_20200814_1650.py new file mode 100644 index 000000000..c6cf3517b --- /dev/null +++ b/apps/users/migrations/0029_auto_20200814_1650.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.13 on 2020-08-14 08:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0028_auto_20200728_1805'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='role', + field=models.CharField(blank=True, choices=[('Admin', 'System administrator'), ('User', 'User'), ('Auditor', 'System auditor'), ('App', 'Application')], default='User', max_length=10, verbose_name='Role'), + ), + ] diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 6bda75b0a..b496f17bd 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -153,9 +153,9 @@ class AuthMixin: class RoleMixin: class ROLE(ChoiceSet): - ADMIN = choices.ADMIN, _('Super administrator') + ADMIN = choices.ADMIN, _('System administrator') USER = choices.USER, _('User') - AUDITOR = choices.AUDITOR, _('Super auditor') + AUDITOR = choices.AUDITOR, _('System auditor') APP = 'App', _('Application') role = ROLE.USER diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index 908978d75..e246ba203 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -111,7 +111,12 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer): role._choices = choices def get_total_role_display(self, instance): - return ' | '.join({str(instance.role_display), str(instance.org_role_display)}) + role_display = instance.role_display + org_role_display = instance.org_role_display + if role_display == org_role_display: + return role_display + else: + return f'{role_display} | {org_role_display}' def validate_role(self, value): request = self.context.get('request') From d5b9596e7d9390d7cbfe258449070cb9da531ba1 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 18 Aug 2020 18:13:19 +0800 Subject: [PATCH 02/14] =?UTF-8?q?perf(config):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 3d8b6098f..dcbf439da 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -247,7 +247,7 @@ class Config(dict): 'HTTP_BIND_HOST': '0.0.0.0', 'HTTP_LISTEN_PORT': 8080, 'WS_LISTEN_PORT': 8070, - 'LOGIN_LOG_KEEP_DAYS': 90, + 'LOGIN_LOG_KEEP_DAYS': 9999, 'TASK_LOG_KEEP_DAYS': 10, 'ASSETS_PERM_CACHE_TIME': 3600 * 24, 'SECURITY_MFA_VERIFY_TTL': 3600, From bec9b97092aa617de4faad9ff6b904cd8af19793 Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 19 Aug 2020 20:11:53 +0800 Subject: [PATCH 03/14] =?UTF-8?q?style(ticket):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=B7=A5=E5=8D=95=E7=8A=B6=E6=80=81=E7=BF=BB=E8=AF=91:=20Open:?= =?UTF-8?q?=E5=BE=85=E5=A4=84=E7=90=86;Closed:=E5=B7=B2=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index a7d0b23aa..b0e758446 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -2584,11 +2584,11 @@ msgstr "不能操作该工单" #: tickets/models/ticket.py:18 tickets/models/ticket.py:69 msgid "Open" -msgstr "开启" +msgstr "待处理" #: tickets/models/ticket.py:19 msgid "Closed" -msgstr "关闭" +msgstr "已完成" #: tickets/models/ticket.py:22 msgid "General" From a38a1868ca671cf000f4fb620ce4b20b33816c51 Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 19 Aug 2020 20:15:57 +0800 Subject: [PATCH 04/14] =?UTF-8?q?style(ticket):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=B7=A5=E5=8D=95=E7=8A=B6=E6=80=81=E7=BF=BB=E8=AF=91:=20Open:?= =?UTF-8?q?=E5=BE=85=E5=A4=84=E7=90=86;Closed:=E5=B7=B2=E5=AE=8C=E6=88=90?= =?UTF-8?q?=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | Bin 56727 -> 56733 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 1c1c347551b3c911cf5ad7a019cd1313f60a8cbe..c5b96696ca0b9b37c9e1fca5948e2dbc149aef19 100644 GIT binary patch delta 5119 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 From 25dcb9510c516c8d2677f67119c29466399536bc Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 19 Aug 2020 11:17:02 +0800 Subject: [PATCH 05/14] =?UTF-8?q?fix(audits):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=B6=85=E7=BA=A7=E5=AE=A1=E8=AE=A1=E5=91=98=E7=99=BB=E5=BD=95?= =?UTF-8?q?-=E6=97=A5=E5=BF=97=E5=AE=A1=E8=AE=A1-=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=91=BD=E4=BB=A4-=E5=8D=95=E5=87=BB=E4=B8=BB=E6=9C=BA?= =?UTF-8?q?=E5=88=97=E9=93=BE=E6=8E=A5=E6=8A=A5=E9=94=99=E8=AF=AF=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=20403?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/audits/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/audits/api.py b/apps/audits/api.py index 0233bc1b2..71b15ceee 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -107,7 +107,7 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet): class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet): serializer_class = CommandExecutionHostsRelationSerializer m2m_field = CommandExecution.hosts.field - permission_classes = (IsOrgAdmin,) + permission_classes = [IsOrgAdmin | IsOrgAuditor] filter_fields = [ 'id', 'asset', 'commandexecution' ] From 5a9c91d9dd1fa3fd268826884a516966ffb6d3e2 Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 19 Aug 2020 13:10:06 +0800 Subject: [PATCH 06/14] =?UTF-8?q?fix(authentication):=20=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E6=88=90=E5=91=98=E7=A6=81=E7=94=A8=E5=86=8D=E6=BF=80=E6=B4=BB?= =?UTF-8?q?=EF=BC=8C=E7=99=BB=E5=BD=95=E6=8A=A5=E9=94=99=20#239?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/mixins.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 38a8a852c..e2da6b8e5 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -7,6 +7,7 @@ import time from django.conf import settings from django.contrib.auth import authenticate from django.shortcuts import reverse +from django.contrib.auth import BACKEND_SESSION_KEY from common.utils import get_object_or_none, get_request_ip, get_logger from users.models import User @@ -27,8 +28,14 @@ class AuthMixin: def get_user_from_session(self): if self.request.session.is_empty(): raise errors.SessionEmptyError() - if self.request.user and not self.request.user.is_anonymous: - return self.request.user + + if all((self.request.user, + not self.request.user.is_anonymous, + BACKEND_SESSION_KEY in self.request.session)): + user = self.request.user + user.backend = self.request.session[BACKEND_SESSION_KEY] + return user + user_id = self.request.session.get('user_id') if not user_id: user = None From 11493b9f3dbfad1ef2bf8d29080d167c43ef15cb Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 19 Aug 2020 13:49:59 +0800 Subject: [PATCH 07/14] =?UTF-8?q?fix(tickets):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=94=B3=E8=AF=B7=E8=B5=84=E4=BA=A7=E5=B7=A5=E5=8D=95=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E5=85=B3=E9=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/permissions.py | 6 ++++++ apps/tickets/api/request_asset_perm.py | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/common/permissions.py b/apps/common/permissions.py index 43aa3fe3f..40f665af1 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -182,3 +182,9 @@ class CanUpdateDeleteUser(permissions.BasePermission): if request.method in ['PUT', 'PATCH']: return self.has_update_object_permission(request, view, obj) return True + + +class IsObjectOwner(IsValidUser): + def has_object_permission(self, request, view, obj): + return (super().has_object_permission(request, view, obj) and + request.user == getattr(obj, 'user', None)) diff --git a/apps/tickets/api/request_asset_perm.py b/apps/tickets/api/request_asset_perm.py index 5b62b8dcf..d33ea34cf 100644 --- a/apps/tickets/api/request_asset_perm.py +++ b/apps/tickets/api/request_asset_perm.py @@ -8,7 +8,7 @@ from orgs.models import Organization, ROLE as ORG_ROLE from users.models.user import User from common.const.http import POST, GET from common.drf.api import JMSModelViewSet -from common.permissions import IsValidUser +from common.permissions import IsValidUser, IsObjectOwner from common.utils.django import get_object_or_none from common.utils.timezone import dt_parser from common.drf.serializers import EmptySerializer @@ -31,6 +31,7 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet): 'default': serializers.RequestAssetPermTicketSerializer, 'approve': EmptySerializer, 'reject': EmptySerializer, + 'close': EmptySerializer, 'assignees': serializers.AssigneeSerializer, } permission_classes = (IsValidUser,) @@ -103,6 +104,13 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet): self._create_asset_permission(instance, assets, system_user) return Response({'detail': _('Succeed')}) + @action(detail=True, methods=[POST], permission_classes=[IsAssignee | IsObjectOwner]) + def close(self, request, *args, **kwargs): + instance = self.get_object() + instance.status = Ticket.STATUS.CLOSED + instance.save() + return Response({'detail': _('Succeed')}) + def _create_asset_permission(self, instance: Ticket, assets, system_user): meta = instance.meta request = self.request From 64d093e67739d2a30351c69cc2c41e12e2bcbb45 Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 19 Aug 2020 17:38:21 +0800 Subject: [PATCH 08/14] =?UTF-8?q?fix(users):=20=E7=94=A8=E6=88=B7=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=B7=BB=E5=8A=A0`org=5Froles`=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | Bin 56733 -> 56869 bytes apps/locale/zh/LC_MESSAGES/django.po | 112 ++++++++++-------- .../migrations/0008_auto_20200819_1732.py | 18 +++ apps/orgs/models.py | 78 +++++++++--- apps/users/api/user.py | 51 ++++---- apps/users/models/user.py | 18 ++- apps/users/serializers/user.py | 11 +- 7 files changed, 187 insertions(+), 101 deletions(-) create mode 100644 apps/orgs/migrations/0008_auto_20200819_1732.py diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index c5b96696ca0b9b37c9e1fca5948e2dbc149aef19..080d1a348e1567a19afadd49545a448086844351 100644 GIT binary patch delta 17081 zcmZA81(a3Q9>?)B1I#eY5EFFFP(w2e4I)FgbW02ff|N9p7g3OqMmnTRN=gLj6bZ=@ z1*Jn2q*N4n-{0MT)?&T0*5$v~w{ZyG#pZ#Y*Qu)KohRN|-Sb)oc;3_+p7#gwy_%jk9)GIk zc_Xn)ZO{7=@8Ajg9jxPdQGuS%n^w>BPSdcmK8wH)8hBn1PQujq8HVCg%zzs(CmuHM zU^H>6hMt!h3nJ_Hs-gB!8;jsj)P1Y503OF2%}378jqVG_>3E7-HK z=cUDZO_&VZU>J5W`HY4b4$2OhoOh3#wmV)QOEk4KN-x;0)A>%|YF_(p-_^M zrbX?bHfpC0Q9Edf>em&uus)atM`31MfSPzaYT^^9m+>NMoS-&tqiIkZjKs|P{1>94 zm#aEz;<~7#Y>axgiKqd3q8`Nn)PhD@ehTIyo{c)Gov5QefjXI=Q2ig8UR$@3bQrA9 ze`YE=x|~X2NsPh@sDT=xcASXXX&;LxU?$@EsGV=c?06hC&Rx`}q~$OG3RX z*Dd}JwV;qhpBp$b(G47f+ED@25x#*MuqtX{O;9`Rg39+oO*8`Y<3uck+psKNLv0{y z2foZO1~u+w)P`>Qtnvu8vzMreQg(DJ%!b-QEat>`tb#~uoo~ zCGr9AHlj`{tdsjOp9QNB`^s2i2v#C73w2`>>QiwQ3*c>w!#lf)GNN{p1Jyqk^%BRS z-jymCi|sHPM`JuLN1f0`WTQSW*}HC_jHsiGMm>U}mal|C#I;ZpHL!e3%O_%X@?B8h zAEQx6y9d?(I}F0}s0IIoI-wi>JkS3j6?Od68oVxi1c}2?M_w5<;5(=z?2TIB2-G_; z1@&@$j={JS^#~83KDHN8kMIua{>K=M0bThxGrt!?MQ?j<%!cJrD{O&!OW#4=*b{X^ zLoqpyxBO((eG4rAHR`@S79T^6cM@ap5~jp|(5Iaxf6qPZaMX@6qVoAL5KEe6P&bxG zEvPDLr;RWOJ7W&)fopIA>d}<$=62lFY=bGtck0ITkE7C!gjP5gHNgthPB&l(?nb>_ zN3H!w^M?5w7NGqZ>d0fdyCaWBJ(>!raavfu1BMd!=+60Tq7SU$6Vw8xVIiDn`6H+! zKZE{fiphxYpibgG>Z|xUhGJw7cM{QLjjN{HwJ;MNRDWa_>ka>b@A%yHE)A<23=b(-xQp+o3*9Jy9n&85!T_O{bz~Hy^d4 z!>EDIpcZxuHDT)a-LK^_s0nLe3^u|r?2B5s57Xc{)BK z`v&#)9zead*HF*Y>+Q^p`mtOb^@wVs7S>8L^uN@og>FD?U>7FW z=l=*5eSW{g+;|r?VCp_@pzNq;n+vt0{1}dLm<}tW-txv~Ppm^c7WMHxi+V>cpeDY8 zTF71WX$Q}!=*U9)x*cZ1aN;7Ug(RSEXn--;2DQWCSOQ03dEACt;2)?7|3V$P*Uz0m z7%HD0BQSeE&R<7U%sP}qO;iQ7fCi|YbwC~cFw~JwMNPB{wWAHFalXb#Jb-!$FPaZ9 zmN>Y-JF%i>LVwObhK7bDbOL>_D2_xeV1v02brNS$AD`>yOVqDqSqHeCl|ns|H_iH} zN7vr$j9O@SOpX10RJ6lUs3Tcm@kZ1{$1xAyM!k$-1Kosqu?TSj>b{<+M=}z1#G_FY zO+-yJ6ZI&TS^Gw7_kBer0}cC7FJThuEq;m`;HAYW2f3q;z~bbyqfV$HHpY&aAGcyO zUO_#||4=6z`hgoa3+mD3K^E%s3Q^G!m9U0#uEDE>nxK~DTcZ}#0X1MZ)Q$$D7Cai& ze==2#)JdJN{4aia{QMLpXOQ7_jRRR3A1 zBVLHQZ>_l%HPIf_iJd^5zy;KTZ&~~leHtjmP&Z&0>IfrIH{`|4SPHeE23QbVqjoSF zwSc9l1#U#0;8v`LCowBV4s##JIMl*wqF&Nw!+8E$QCkvOum|dBKS3>E2de!TY9~pk z8?T}!zH9L_YyS^*V!^}Rf>NX2kqFdAb6A`gwb1zCoWG8=4v7NzHkQWGs2%S@-FO7G z!;==DH7}umL8t-mS{yjSeHv1s?kj=XP+3&}TBws~=%b>yvK{IK`kKc%nhVhX z`9-~i8&M~72sO|-YrlpX=XcZzg|I;_AOkASiaN>ssCj(xmUzQDR735&5$Y)0peE>u zy0JIr!@(GXi_QJ06S$7q@L$Z1nMb-4E`z0rYoUIj8G)RL&s$6-KZ#A46EC4A_!rw^ zo)7t`;V7(v_wfUa8|A)eR$&|BqZo^MKXMb-!(znUkZ);k9u~rMAG?>a0?iWSFl#NANOav7@s7pRjuVxGtJ#Me>%{zg5DfKS{H zsUXzpdReHXz(Lk99CegaF(a-)eZlNUJ;NVRFVij5JMj!PaNt;X)WN8HCJe?rsQU_| zo_RUTS4E#b?{%r@hPD`l@0q<(@4_I|PDY`2IvaD~W-N$jPz!p28ZY%Y7e`|ZaUANU zZHU@XXViSd$8r9eU>pfeJOee*JnOI)Lx{Iycie~CX_4`6fa0iyRkXON#fhke4#ZSA z6${~f)c4C#)B>K5=lu0C3Yy?no*uQre5kiK0X0BdYwwDo#6wUE9fSJ*n2B2GN-Tt% zFgISde88u!Unr&|pA9unUY}Lo#8M=hp^kO}>YZ4F3Ahn;<3r2;jXJTHsD%Vgbk8~i zh7#vMEvO)N$C8)_KSzDv973I>FNsPRmCNQmtVsMPYJ$>}+}r*p79}2pIdKJU!lS62 z4w>wp@j@(5yczWfen&l`kST7$Xv`MCVvr5`yvkGEN^7Ea*a&rFYt+#Wz_d69BXK6C z!8NFz?m+GI5bDHEVK|;g^}CH)@H5M2n#SZTpdh}b&wqpIS_Z#*VJFN!gSR%2w;fLt zFP+KXVz{CIEKY^^yV-pE;lVld!HRSFUV(vq|D<>^JZ(g(f1M?Z`QN6JC z;AI@V28>$fzVULR;_|2gYg%02;LXU_9!CW?+8YgRSun>Z`lza@TM8 za?W29eL^B1PBOQl@>fs`x{3N&K0>{O;VWD|uUXtoFzaCu{aT^=Cz|~+HSt&s!kIp+ zEI_^G%TPPmf;n+FY5~8XcJiCW8CJS{Zq&kxS=_*EWBK<`l3s zvfmmGV-WFKi!Y%*j<>N82Ci~Hzl&l~;zp=Tl^LN z(rBJBucDsSL-Rk&hpll(n$^r}#+l{Js;C9Lg_W?W#h+RGd`!#y-U@5jhT7>js1<%| zUN#?M2>E1d-FJCv)C9%Na;V=MDw(Y;-xqb?VDlr?JQLCX`~O@^EXGtctVa#J17mTo zwckd)L{Cr)O#X!%D5IIjj7QyH(c2 z$MPxGyMF0W1LZ>9UjVhx5*9Z`jnf9T!5*j$jYi!!&qqZQE-^QlyUb(e1=K>Xo6jsC zvcV0U0X0sXS;4H0ny9(OZ7uGGsmc5LP)Vx_YJkb+d~-GGi)fpfghhy-qXy2uk$=3z zI8=NLHO^`CvgPk#TJnEbK46o-AAkR&lADICsPEp3LVy?ZrEz^9*iJ9j1}<*)IgcGx_)`h zIMn^+%t~fWvp(v+W~fg=N7P2fqyOLkH&};VsK2itv-l=vCVqsPAZ(j^*&~Q^}QE{xrC481Bk6L+6YiMM)#De78qZTwCQ{n=Q z!WHOW0P5}DkNzVzFPryJ_y39dIH&sB_45^`q5;aGI#fb^8k(SvtOaJkt{90v%#719 z2G?2pNz}wQQR6&9^-HnS<)cyg!WJhW-;_SD9u@7pJEq40sE!jXztCKTTIiRkiFTNW zQT@-Np7|}yKg0aQ$#%K^g;DdAL!Dq%f1c-G&pNb14fL+XgUvChlbCKUv;0<6|NW>( zbRM+;{be8%q{Gr!0JX71%!&h1-y72uGrxC$N(uZK)iH9n>rem{w?^IA8@2OKQ719S z{M_;@Q9ImX@osBBj#}s$^QQS1`v3hubdOtk4znoghKi^O8=37;6L&}LxG!qL1?Ea~ zlerW1MRN#q;62p1A$#38+4l1MHDGQ_6hw81M;&=Ji<_Zdrmk2AN1+yW!n|bOHlLuz z3E1a07KMuQSX>5mf6aY9S7}NjCy5TI9esoaa3Yq(?Wl#`Hy@)0c#i5Hyx+BlqaIZz zi%X*VyK)t+$EUt{2u)f(2 zbL#Woi;6ld{Z8a3fH)B+xu&rnDH!VEs-;&4>|tf>C^QSVS3>clFc?rVbT z*A`PUzt_zg`k~&^;nrb3wj^GSg)#6jzkFaEYJkS5{_Rl%kH8=tYfeV(c&5cG%yp>y zw;C3wNF8x z2A)MlD_w{B#`*>|@DtRH0Y}~EJqqjPqB= zB_y=7)#iTd_#>+Qj^!UtrCmL zXeeTFyjd3Y)tg{(E7U?0F*y!I9pzBe0w!S?F0}kw)Pion**qmxEG}of;`wBz!`QKv=$1pz)7f_$`mu9Y$ zZU@y-3uuQ~a46~vW+v*sl^BhuQ44-(`4_11Vo$k#wXp_qUo6M`-VQ2d@iDf*!l&J@ z&mW^cj!RHS{sX4P2N;7du?WU|@1Aje)KT}wX*dr1VYV~w5lu0dqJE{@hCWqJP^pDC zusRkw>yEx3YU1IjiKk;IF2xAkfZFMP)RCS-ozMk*6CYbX{+#Py7WGSOT@1&>bDX~_ zeMxAbk5L0pwRjn7;7zD^;VX;xo5xW7Ph0zS%m0Sy$Um`siu3ONw5ahiqc#?Ep7Ynv zN|TT^P&;UjdS?Al3!7;!H8-M;`WuTcpic6-#m`X-4!+>-4>xm|g-{DA>!Z?yN`fV( zn{!b+TWs+bi+7?1K8gB1NJ70kSImqT-GmKM8)}Ohrzb|@NQ-Bn#__GBqK=y^vCrb; z=8vco_!afC{e}8MiA{3(T4poUfE`g2_P|W&!wNVXv*UTxc#pBFKL6ei?kK9EzJThY z25Nxn*vi`9HG7)NBsiw7&UOj6?Tuh-UxK2ba>^73(=UL@*X9E(up?x z#|m$ObrL7+#_rT>&`H-<4*#E}XdgvMWBn?V9YDE9-@@d03Z9SpCF+%|&new6l;TH~|->!c1JxNhD+9BXxvnt{_Y`DyOa$MXuFp?q#* z{D`ZqKfiN$lPUk$Cf=dVw~U}Ul^-nmt95?GPRpmJ&lAc`imrv`8QR~bETZV`KR~`G zr6FY>@lwo6X+f#MeYyR2^V5>}J^lZG>eHzM$sr^rVrk+-)N`Y*->K_L&>!sdrsS$^ z?aN*0?ZqFg?^4<(QYMfKBiEhs8}&BiW>QvC-%74o0Ox;?K>yvYE{PJ9dc>tEA5n%= zvXT23%Tw-B*Hw@HjVZb+TW$vNOX~e8St$Ql{$279tgrg-w>n=F-kbWU=xRjg)po-t z#5>4uwpi_dQC<*VApa>P6(^x^)5$Y=`L6mCbl2aCuZ>l~N zUo3wV*Iit>Xk1JQwZWBZsz11MN{IS>jsM|IqW-Rh>iu${or`;)ghnqANA0(T~ngiNCS|Hj&>zIYilQ zxft_9a&eSp^iLp{&c@*E9py*JV1jqI9y(?~(0KM}7u*O~~U}A9h&NGklGD|RdLzoO#7QpY9jD%n+;SX5E*K9G zZ=qflUtQV#KlSWr;%L}PDMsP9Ebn*gco64Mj*`1Z*-Skj1O9F8Rq$uxQh1T_fIheJ zOVpK-wmFoYl>08_4WpiyKE9Uxp=*`Hi=g3G%PBRQdS-I1ESHSBEoe8~>45(kdzQ#m~{^u3C_)W|k zWc91qo46ch27P`cUP3(!B^UK7Q_JVqMBv>h-x{1LYlJ zT^-Di&6?zXqkR^oHua&DTGYSBSJxP;L}LotbbV*{tDZ;S|DRD=Kw}wes6^bE`n%*t zQ-5{MB(7+KC>O#BrJ~<{>L0$AS6d!RJ4zAq$86krul4CheFDYzmo?@g_=tKkN^6b4 zb=BejWpozx|LAke`p>7X>n+N7N+S8Rlsc4klz!wI)2Ak7J@Gc&VfQ@14dl3d{(oXy zPi;GiUno&DPO*-^;3R)Pc1YVb;)leeDEdDv*TRp;eNJ08>R(b%i*HiSkz0z#$Ze$b zqBJ1)F1fF$U!=I#SaSXD@S4$(pR(BEsnpX^{{a7G09`rF08_aV)Pq#vDoOo3NEuJVcf|i;H1X?Kb%N%UZ)lrDd3DvH-?3K#p0xWO z(`Weqjqw?AX6vgDS9ALKmXM646eRIJr6b8*xP$WQN=y4>^6gMpFn9lg*DY>H+t0-H zDY|0FH^D#fF@8$fPJJ?^4CM>r)RgHu|5GGhzfM~)gh6%fru`B1xz?|X`3-IRsYl@7 z*ayep7nD!v|0i)*>bjm0r=gUmv~?+O242wb|6Ay^k066J>>*CZ4#yGyOT8=Rq`XD_ zCVb?g8~-LgLV1(^y1F^M!L$w6=YNegDF2lB8b#MW z%dexJf%zA{1R+%yqOc)AOHT3D?ZuWQYsB3h*VR zHJ!&(s?+wAcrNy``-TyR(iTqKp7Mma9&vzjJc2K+A8GGv>gUK;!!t@SzyFFPS&XvP zh5kR=^XDUSx-#KqyupoiF%=~%pH<3^!di_YfW9(c}idEyDXm@QxFgJSwfxe zQ6EXNm;$bT0^mAJwoI<-GL`V&_Ha7fLxy>0|kn)MM$_igKLd z`;Elw*Fl1#bgqcctf3ADTm2v0OZ%4B?t6z^OLBL~Rkev4kPA|s+-;ajiC|IJi9aTP zAA1Jy7rIDq^ZLr|;*&S)*SYWJ|N7Pqb+yVPR#r_hW7Vy(+c*ERZ*eNC4*o7r`2PTk CV+Ire delta 16963 zcmZYG2b@hu*vIi>v1{#O?do+|Wm&y1qW2a;5Iv%=L`1JQM6Ze7iC#ihh)(q0MT?M# zmLLQvy7%|L^K3qO-}ihzv)_51nKLtI&beo=h-Etx&)=TdcP%{0bdMtu_#>=ku!6^SskEG;82_X)*e3&kMu~m;$R~2sXxa*cNkO zUvm~_C*Fn`@dsr6-k+#zc!mWrrlC9UJ0VA*urolECguPJb4Z;ZK_r_4s70FEQ{xlV!jd+1rbO*bdej9(HD&*`;vyt;56fT# zHb)KE9d$1Uq9*cLek5w3shA5FqONd17Q|B+jIU4=1vPX1(x7%G7PaGbnz8>Hpb-hJ zv@fPbA8LTddk!gix3IDk=j7E9vaSR4y9_q?*$4m06g)Onjw<8Sv- z(Y-x@Mew{e1oD~FL}92aOONW86SZT-Py>`j4Ok7eW3^G|H8b0w&g+ONu@7d&5vU9I zEvBLY4xt7-j_L6NYO5Zg?&TXZbqg0qnI%!TtR{wIOVkd2gc@&zImw)5E_V97uc&D2 zH(@M(hq{7is4Mkax+_SA>X!kvupF2Pi(y8rkD9nMYT_ZNhjBD&oK2_;-G#cqeV9?7 z|0`7VaQ%gv_yuY!16sLz8-^MnE9zF{Mr~;k%U8mj#I;a66^GjDA*h|1fa<@V6qjqLEY6mBxu6P=1Czqnm z`^MaXk;Dhvu>abMJ0vvVAE+&RiTc=uwRQI_FX{>kp!&yJTm?13+oQR6j3o@cKeYNvK!7Ceeo(09iY`8#-CB@#7JCyqgVD!#ycIMd=isC#?}btNZJ z{V$*%;-65@%45ufDLcAXdr2%x{4Q#TMk5#M^EOb?K!;FUc?xw4u37#M)Pzq_6TPu~ zvQ91^hFQs{N4-BvqMnr=sQ!a75J#fsnSk1%Y5u&A&m|RgTwx9ISb}&5YRey?28`(J zwlEU4z(S~Jpc3lgYKTD?hq{HmP%o;{s9QJ-bqkhY5U$6J%fYQ%tuUyI zdj=vPi!Jbz7Vkvk_;()mRR7Yi?r@Ch6u3Ma`F{8~493l}seG!aAr4 znxd|>Ee7L9sE4b+wU0Nane#Cp?aNVHej1bGb=0l7j~XW^&gE01=F1Yt{%fLWYbcFc zKoyL^x|Z*U+VbJ(zo(d(cou3W=AmB2t1$%kp?2aF=EMhP>h5l*N}$dw=cA%~UmLaJ zcBprG9BN_1Py>99+QJQ}kJon8j$E|-UDTF7HlL!#dySeX_(L~Oder%aPz&|dp^}G6 zQ`8AVP+K(ywG&e;o@?#PP!q>vDDJcTY1FfD1@-IoAqHa5NA5yWqCQPoQ9D-=8QKd6PJ_}G1FB2fz{g8q*u>eg07|3i)1 zv9_oS=#EMB`R_+XpWne4g|jgg?oRJ3JVQCD~vQ{z?CLLQ<{c!QA``iWauK`c&O49j9C)B?Xm zO}G-ZMoQDuKEMRjj>%wKv6d&0+Mx&d4to9E zH=o9sop3Pf7S2OGQ>#$pZb99;y{LsAL3YUJow0_C)^H6q!EMVwMJ?zJYQSXu-4&%r z4IG8)Ul7w^Mbt!1P`9onYNz5XKhzwFDfRiEKqUhm=AyQCBkJD$f|}r0)WDCiC*hwne=+CRlzoYTi7!D=L~O$w0T&DNzf^i2D3zM?GA* zP$!l|ZE<Z}VRy`o>oGl^KrQSR>fS#=E$GD{_CFJqf!8LC5P z)Rpu_o#;bd`Dlx$S^GTHjx9qiXbt*bJg5uYVevlHLQkP~^w+`ce?BVDNR+~;A?}J> zpib zFw3DHzAC64X@(l06Y2_kqt5>nwKH>36R)&*Eouk1p`Mj}7N4+o-vuhV$2U-0_y9G* zpQsZ9*rB`_f{|Fu$FjpcuNAhyJh&M(@ii=hFR?ck{LFoYTa0=LPvSbfjjM6` z2tDMiuF^=ig)LAE>4O@0K9h442uqD}{VSk$>OHeFrX}u!>Ng&BE2d%u&O$Bx zD@=;PqscSBmx_wEG77cQ@~9U}6VyG8Lp?p0kxpXr~wyRydEQo_n;oy zUr-nH4{E|xW8L_fQS;>)%l>Pif+W=8Eeyum7>A8fSNa{QeLrepr!Bs1@nh6NlZ|r| zL}3hZA=E3o1?v0>s87)>)WTPcWB;|ntt9jiox$Yz&^kOvoftaaU2z7~mPcbS#$pUs z#VG7y`KhRW^D!B&L(Q|?P&+mYgK#B= zU_5F;J1`CpVlFH;(Y(HgNjbPk6{?}g>5zJV-t-j zu{`QZ>!Pl-8EU}yF*SBZ^&5aWaE#?wV=fl31M8E&K1H+g=Y**|$f|u!B{G1knZ|=f zL&+Ho&Iy4td2)%{%wkY%I-A>pr*S7Xp2LGlztnR*?>U~C$9D(v#}~LQAGXjwrl^I_ zM%~h7m>Sn%S|63&s^A68j`vX=LKnFcvSEJWyjTq1K|QP^F%K?5?ch<2#9OHLz%RKi zmQN0Zq;OK zpKUHjz3?_HVgEJ3aS|HvvUT{`;@{0b%@@`lu+%Lq6>5Sks2$3K(bx!E;6T*Fd>+*= z)iO6vM$AhbxyCAy;?rU4j((=aFQ#zJ@l^)U@!>CTHnO_1L#japzeYyZIPX6-%Ap)T+9 z##@Ie=4|V*$l|rA6>qfs7V`k=m3kI6;a${)1g~-n3PXLWvZAiMIO_dS!+h5-_rHTB zdZC{B;pXR-Ux2~n*P5HnJ?1gK0{3onOG>;^<3GqLMY##XQ9CScd_q z6^}wKaE9eqnVZagsDV#ge9_{YsPpbw{)WDNb zJ2AuJMHa8Hcs+*FzQsI%d5OwAtHlp3eu*i_C;r+kFg0o+z8qF5WR^p{h-#VLupsdS)WF-2Ux3~oi(AIK zf!dorQ2hpDD*VjyQ_V#fMSd;vYUa=XRQOHmg{*fcE;rYrR=CmJgY}3{TAcYCw?olb zgnT)RyIbr-U2!~WLAy{Fb^!H0x#Q1s|L<9c$EXRPp&pJvepl*a7ls9}Bx<7es6Q)C z#XNWv^>zIb>bzup3D7v1N3F4p`|7IVMgK+s0kLJ9=2tuh3!HOe8#+n>UR(I zh2t^mcSqPJw?i?g{_RoknI0AoL_Hg$H*xWcTH+E1G2Q3L*n+3`12 zzu?Vozz9_TC=A2=sP=LgfYr@fn3TBgX7)cNl}03Vh3!xS^g`{#K;+AjHx1Kb=`F6k zzS+cViIr(@k6PeT%dfY1i^T^lK8{-0WuGCjorJ|3+E!39%hUxG*Mquh~ZYOeMBymMldpp#` z{ZJE)z>GN4^6M?X%i=SrH{~_dmA^uEl>h%@yBi=Ys-cKk7PZo9sEO*D%~2C}L_I|P zEk6dMiKkn<3pL&`Oorzzf6csy;mq$nwM57cH&F)EPUJRAS-vJ};wGqD)ETvaDVPBl zV=3H@dR87|W=zH(7WCf8g^J(B;@A`Y-~X3chwVzxa39q%V3%7^Hq=hUn8hp~i@L%Z z7B{r^)~JPcF#DO~%=xH=$D>b`-PUj#wbeJwKTs3DLS1o^@7#oi%~-RlSs(SHX@=Qw zFlr%lQRA#LzcqJ!$NsCsJ`&pU3#j-G>S210bui6tx3IQmcXNO_3U%I8)CDa^#hXy^ z5!8iUHgBUo6;F2i+!dwWB z1za~Dp%#{CuNyy&&nlVCoTvfvp*ogA4Nx03(R&v6K|ORMF$XR{owwWaCoH~=I{%5q ziTAm2!c1QQDjKL3>Wbe-4b;W#V-C0W$rjHrm!KZnc#99BCOl`}KrQeIs$bgu?!6L& z{BrVn6{%>#&Zq_SHV2`$dbl~koPp}U2-SZr>e<6YiW=w*Y9WD#-GC9Od_J=XYGI{O{VJOcti2^_p7xgS zbC~^C$KjTkjQXuM54Fk|jG1U37s0G!)T-d_$K68RO8+D79Tf9RZG|&-C zoW`WYS1o@7lM~-XP4pLPX9AA7D+$M7;uzGeDv1@b8LIzG)c8xyHJF(A8;gCLtg;jJ z%H41AHPlLPVG?|Sx_91jw}1%L1W~Ab5!BXJwR|ho!n&ds&qlp`!KHmNOa{uR0(aLwC7H|PG z;SfRniE%aBMf{D(stvC&J3zD66W=4HQD`Zy3TEwleIxa))%suq! z9{)u}6Nj90|D`fBh7;#SU1?d=mexe=Py>7m<1D`l)juBfC3PRB#!Kd1)Hp9u{er)D zahC7-{A=KRB($O;sJN_I8FfM}Yj0`!ju=M1yXA+Nqpkf5)P>DLUD!Hv7wQ5|qHfu} z?`yi1q&)A;Y(}HDx}?PoP+Qv4;!jWu_E|i3>YhRoJ}o_-Ok>=PibF0q!%>%#(c%IY z7e|d$1NFggh-I*;IT^Kghf$Yu9yQJ_jKHTBhhAhaHBk18u46t_9BXk^voUJG4^U6n z$Ec@kf#r9bCs61AfST|oYD@mX@)&l>J;wD>vPQUsxEJd8 zV)`p?;Nn=ExCSae(DE}-1Fph!iMXYx1swX(E#R1W9EBW$91kq1 zSm$K66MIo#OIvSZo*Qo@CMEuv`X0(~>ZK?{DOD+BX#b8flX`2~>roG(&bqu4)Dw=D z{))}N%(AWNuz=19M-A$iDJ|^8wU~l!(9Y=Kt>!=eF-7=)&i{b^6Ua@*%GT$1tV+2} zzvDiBRIw9I(P;tYCh-M~$oTap|etpR= zqP`nL(SQBz>ClG+53&EqM13Kp5M=_%P1JXmZo7V`g#{h@&W9QXh+D(LZBtDpg2)PI*G%ySDcmxw@2}i67Y@ z6;S^Qs10Q`^~6k^a8x9hiJ~Jl`P!U!mGYeUGs_<&{*$7QdnL=A(e`a6_y?2QfbY`b z0J*=g9eEuglt9|I5I?5GQQDFF(%Pwdyh8pxMpDaRlf+P;Mv1V#jqw2`B{~27kEPOy z&K>Yi$`I>V6i*QMp~O)x&?mVKO6aw+e)_KUhWxScf54 zj$9gwj+wN*#PZk$50d|u`W|anAN>=?`KtV%W0l4Gum|Vtpd_dMHtKjq@qOj5y8q?m z3In~Q47A*P*q^eL@-F!)cA}csQwFKRQI9qq)37AQV=|tby7b>p$w1teQlGYeP{%M_ zsrOGJIy55~{GY+qp%Za6@?TTFpy)V}CBo5ih5$k?wyeE4@p@HA)*AbkwKL7b9;fox1rm{2-T~a+vZHB`c|qY2Sh8@pnpj zijGQ@>(uws@4rV?;@RZ)QNE|%UGKjF*6A7b9W>M-{>pMXITA{0^lFr3&Q-a^v**f1eIlG?*RL%79}|wt#G@^)O)|4JJSV@`>VN3{|B^&YI#xm*XQ(%z?6#8+xU~Og7IMLs&rbi0 z)N@ld+PSx}n6(cwGm`s&`gukeY-8QQShef^_oTeC0jPTX8C?H|viBDql295`W>E%H z-lyEBEf-}Q=PbvkcJ6#kMVUY;NC~EGKK(C()eJmD1We z&7j_wvWxa0yZ40u?`TZsYsyOct>)b7)bCN|5GNd4$gd)PVu|V0!zgd-`tMlB2%ESa z=A*o!^B0u-ln@q>ijvOyrpKrBX@WaxuZeF_KBN>QUX8_Z9px_NGfHA|wK%6RMaMV9 z{{0`|ukejmF-K`Sv>~?*Q&EyK@jG~y+%(EY`qaP&{@#4Ir)@lOQp!)%6OP=(O)YLf zeJ4f7X>3NhKzXF!|C>pcr`)C-Al7k_6CYwVO6bUH+7O4-gy`N(}ky(~7w4dlk*e~+W& zbWAeGxbWYfovDR#{z}?^qW+$~|EDBz!@5j0?~&7S$y9w7_1E}5B`@_6)>e_Y7Xu!l z^dpy@cphaU^`{t4$w_HJ>1OA2$5zDe=>7kK6LmZ@$KWA4l%Qm#Or=~R*9CP9YhUU_AbT>nTeq+30hN+$-v9^uGRxpaIFT)c>IV9rYxb$Zqa&;xQ*KhvK*>YB zGo>c=BrN7v)KSa^RXs87rzi_4Z7rVykJ6S&9XPJhrXvseIn>KgA4SQUh(8i+A~{j5 zb}T0@NvDeV9)3%{9OXmm&B^Jg;qdB{i=e&?18958^3?-2ysq4)=!TDay%n-y^U#G= QH+-;ncJd9uC$gmeAB=ATp8x;= diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index b0e758446..66fb19a2a 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-14 16:50+0800\n" +"POT-Creation-Date: 2020-08-19 17:34+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -29,7 +29,7 @@ msgstr "自定义" #: orgs/models.py:22 perms/models/base.py:48 settings/models.py:27 #: terminal/models.py:27 terminal/models.py:344 terminal/models.py:376 #: terminal/models.py:413 users/forms/profile.py:20 users/models/group.py:15 -#: users/models/user.py:489 users/templates/users/_select_user_modal.html:13 +#: users/models/user.py:495 users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_asset_permission.html:37 #: users/templates/users/user_asset_permission.html:154 #: users/templates/users/user_database_app_permission.html:36 @@ -79,7 +79,7 @@ msgstr "数据库" #: assets/models/label.py:23 ops/models/adhoc.py:37 orgs/models.py:25 #: perms/models/base.py:56 settings/models.py:32 terminal/models.py:37 #: terminal/models.py:383 terminal/models.py:420 users/models/group.py:16 -#: users/models/user.py:522 users/templates/users/user_detail.html:115 +#: users/models/user.py:528 users/templates/users/user_detail.html:115 #: users/templates/users/user_granted_database_app.html:38 #: users/templates/users/user_granted_remote_app.html:37 #: users/templates/users/user_group_detail.html:62 @@ -146,8 +146,8 @@ msgstr "参数" #: assets/models/base.py:240 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:23 orgs/models.py:326 perms/models/base.py:54 -#: users/models/user.py:530 users/serializers/group.py:35 +#: orgs/models.py:23 orgs/models.py:374 perms/models/base.py:54 +#: users/models/user.py:536 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:56 #: xpack/plugins/cloud/models.py:146 xpack/plugins/gathered_user/models.py:30 @@ -161,7 +161,7 @@ msgstr "创建者" #: assets/models/domain.py:23 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:27 -#: orgs/models.py:24 orgs/models.py:324 perms/models/base.py:55 +#: orgs/models.py:24 orgs/models.py:372 perms/models/base.py:55 #: users/models/group.py:18 users/templates/users/user_group_detail.html:58 #: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:149 msgid "Date created" @@ -354,7 +354,7 @@ msgstr "" #: audits/models.py:99 authentication/forms.py:11 #: authentication/templates/authentication/login.html:21 #: authentication/templates/authentication/xpack_login.html:101 -#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:487 +#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:493 #: users/templates/users/_select_user_modal.html:14 #: users/templates/users/user_detail.html:53 #: users/templates/users/user_list.html:15 @@ -395,7 +395,7 @@ msgstr "SSH公钥" #: assets/models/base.py:239 assets/models/gathered_user.py:20 #: common/db/models.py:70 common/mixins/models.py:51 ops/models/adhoc.py:39 -#: orgs/models.py:325 +#: orgs/models.py:373 msgid "Date updated" msgstr "更新日期" @@ -407,7 +407,7 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 users/models/user.py:508 +#: assets/models/cluster.py:22 users/models/user.py:514 #: users/templates/users/user_detail.html:62 msgid "Phone" msgstr "手机" @@ -433,7 +433,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:14 -#: users/models/user.py:655 +#: users/models/user.py:661 msgid "System" msgstr "系统" @@ -547,7 +547,7 @@ msgstr "默认资产组" #: assets/models/label.py:15 audits/models.py:36 audits/models.py:56 #: audits/models.py:69 audits/serializers.py:77 authentication/models.py:46 -#: authentication/models.py:90 orgs/models.py:16 orgs/models.py:322 +#: authentication/models.py:90 orgs/models.py:370 #: perms/forms/asset_permission.py:83 perms/forms/database_app_permission.py:38 #: perms/forms/remote_app_permission.py:40 perms/models/base.py:49 #: templates/index.html:78 terminal/backends/command/models.py:18 @@ -555,8 +555,7 @@ msgstr "默认资产组" #: tickets/models/ticket.py:30 tickets/models/ticket.py:137 #: tickets/serializers/request_asset_perm.py:65 #: tickets/serializers/ticket.py:31 users/forms/group.py:15 -#: users/models/user.py:157 users/models/user.py:643 -#: users/serializers/group.py:20 +#: users/models/user.py:649 users/serializers/group.py:20 #: users/templates/users/user_asset_permission.html:38 #: users/templates/users/user_asset_permission.html:64 #: users/templates/users/user_database_app_permission.html:37 @@ -733,14 +732,14 @@ msgid "Backend" msgstr "后端" #: assets/serializers/asset_user.py:75 users/forms/profile.py:148 -#: users/models/user.py:519 users/templates/users/user_password_update.html:48 +#: users/models/user.py:525 users/templates/users/user_password_update.html:48 #: users/templates/users/user_profile.html:69 #: users/templates/users/user_profile_update.html:46 #: users/templates/users/user_pubkey_update.html:46 msgid "Public key" msgstr "SSH公钥" -#: assets/serializers/asset_user.py:79 users/models/user.py:516 +#: assets/serializers/asset_user.py:79 users/models/user.py:522 msgid "Private key" msgstr "ssh私钥" @@ -1025,7 +1024,7 @@ msgstr "Agent" #: audits/models.py:104 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: authentication/templates/authentication/login_otp.html:6 -#: users/forms/profile.py:52 users/models/user.py:511 +#: users/forms/profile.py:52 users/models/user.py:517 #: users/serializers/user.py:240 users/templates/users/user_detail.html:77 #: users/templates/users/user_profile.html:87 msgid "MFA" @@ -1265,7 +1264,7 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:409 users/serializers/user.py:237 +#: users/models/user.py:415 users/serializers/user.py:237 #: users/templates/users/user_profile.html:94 #: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:166 @@ -1274,7 +1273,7 @@ msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:410 users/serializers/user.py:238 +#: users/models/user.py:416 users/serializers/user.py:238 #: users/templates/users/user_profile.html:92 #: users/templates/users/user_profile.html:170 msgid "Enable" @@ -1685,7 +1684,7 @@ msgid "The current organization cannot be deleted" msgstr "" #: orgs/mixins/models.py:56 orgs/mixins/serializers.py:25 orgs/models.py:40 -#: orgs/models.py:321 +#: orgs/models.py:369 msgid "Organization" msgstr "组织" @@ -1693,11 +1692,15 @@ msgstr "组织" msgid "Organization administrator" msgstr "组织管理员" +#: orgs/models.py:16 +msgid "Organization User" +msgstr "组织用户" + #: orgs/models.py:17 msgid "Organization auditor" msgstr "组织审计员" -#: orgs/models.py:323 users/forms/user.py:27 users/models/user.py:499 +#: orgs/models.py:371 users/forms/user.py:27 users/models/user.py:505 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:73 #: users/templates/users/user_list.html:16 @@ -1722,7 +1725,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件" #: perms/forms/asset_permission.py:86 perms/forms/database_app_permission.py:41 #: perms/forms/remote_app_permission.py:43 perms/models/base.py:50 #: templates/_nav.html:21 users/forms/user.py:168 users/models/group.py:31 -#: users/models/user.py:495 users/serializers/user.py:49 +#: users/models/user.py:501 users/serializers/user.py:48 #: users/templates/users/_select_user_modal.html:16 #: users/templates/users/user_asset_permission.html:39 #: users/templates/users/user_asset_permission.html:67 @@ -1789,7 +1792,7 @@ msgid "Asset permission" msgstr "资产授权" #: perms/models/base.py:53 tickets/serializers/request_asset_perm.py:31 -#: users/models/user.py:527 users/templates/users/user_detail.html:93 +#: users/models/user.py:533 users/templates/users/user_detail.html:93 #: users/templates/users/user_profile.html:120 msgid "Date expired" msgstr "失效日期" @@ -2537,36 +2540,37 @@ msgstr "结束日期" msgid "Args" msgstr "参数" -#: tickets/api/request_asset_perm.py:45 +#: tickets/api/request_asset_perm.py:46 #, python-format msgid "Ticket has %s" msgstr "工单已%s" -#: tickets/api/request_asset_perm.py:90 +#: tickets/api/request_asset_perm.py:91 msgid "Confirm assets first" msgstr "请先确认资产" -#: tickets/api/request_asset_perm.py:93 +#: tickets/api/request_asset_perm.py:94 msgid "Confirmed assets changed" msgstr "确认的资产变更了" -#: tickets/api/request_asset_perm.py:97 +#: tickets/api/request_asset_perm.py:98 msgid "Confirm system-user first" msgstr "请先确认系统用户" -#: tickets/api/request_asset_perm.py:101 +#: tickets/api/request_asset_perm.py:102 msgid "Confirmed system-user changed" msgstr "确认的系统用户变更了" -#: tickets/api/request_asset_perm.py:104 xpack/plugins/cloud/models.py:202 +#: tickets/api/request_asset_perm.py:105 tickets/api/request_asset_perm.py:112 +#: xpack/plugins/cloud/models.py:202 msgid "Succeed" msgstr "成功" -#: tickets/api/request_asset_perm.py:112 +#: tickets/api/request_asset_perm.py:120 msgid "From request ticket: {} {}" msgstr "来自工单申请: {} {}" -#: tickets/api/request_asset_perm.py:114 +#: tickets/api/request_asset_perm.py:122 msgid "{} request assets, approved by {}" msgstr "{} 申请资产,通过人 {}" @@ -2749,7 +2753,7 @@ msgstr "" " \n" " " -#: users/api/user.py:147 +#: users/api/user.py:158 msgid "Could not reset self otp, use profile reset instead" msgstr "不能在该页面重置多因子认证, 请去个人信息页面重置" @@ -2795,7 +2799,7 @@ msgstr "确认密码" msgid "Password does not match" msgstr "密码不一致" -#: users/forms/profile.py:89 users/models/user.py:491 +#: users/forms/profile.py:89 users/models/user.py:497 #: users/templates/users/user_detail.html:57 #: users/templates/users/user_profile.html:59 msgid "Email" @@ -2836,7 +2840,7 @@ msgstr "不能和原来的密钥相同" msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/user.py:31 users/models/user.py:534 +#: users/forms/user.py:31 users/models/user.py:540 #: users/templates/users/user_detail.html:89 #: users/templates/users/user_list.html:18 #: users/templates/users/user_profile.html:102 @@ -2856,15 +2860,15 @@ msgstr "添加到用户组" msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/forms/user.py:124 users/serializers/user.py:37 +#: users/forms/user.py:124 users/serializers/user.py:36 msgid "Reset link will be generated and sent to the user" msgstr "生成重置密码链接,通过邮件发送给用户" -#: users/forms/user.py:125 users/serializers/user.py:38 +#: users/forms/user.py:125 users/serializers/user.py:37 msgid "Set password" msgstr "设置密码" -#: users/forms/user.py:132 users/serializers/user.py:45 +#: users/forms/user.py:132 users/serializers/user.py:44 #: xpack/plugins/change_auth_plan/models.py:61 #: xpack/plugins/change_auth_plan/serializers.py:30 msgid "Password strategy" @@ -2874,6 +2878,10 @@ msgstr "密码策略" msgid "System administrator" msgstr "系统管理员" +#: users/models/user.py:157 +msgid "System User" +msgstr "系统用户" + #: users/models/user.py:158 msgid "System auditor" msgstr "系统审计员" @@ -2882,67 +2890,67 @@ msgstr "系统审计员" msgid "Application" msgstr "应用程序" -#: users/models/user.py:411 users/templates/users/user_profile.html:90 +#: users/models/user.py:417 users/templates/users/user_profile.html:90 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:478 +#: users/models/user.py:484 msgid "Local" msgstr "数据库" -#: users/models/user.py:502 +#: users/models/user.py:508 msgid "Avatar" msgstr "头像" -#: users/models/user.py:505 users/templates/users/user_detail.html:68 +#: users/models/user.py:511 users/templates/users/user_detail.html:68 msgid "Wechat" msgstr "微信" -#: users/models/user.py:538 +#: users/models/user.py:544 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:651 +#: users/models/user.py:657 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:654 +#: users/models/user.py:660 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/serializers/user.py:55 users/serializers/user.py:93 +#: users/serializers/user.py:53 users/serializers/user.py:88 msgid "Organization role name" msgstr "组织角色名称" -#: users/serializers/user.py:59 +#: users/serializers/user.py:55 msgid "Total role name" msgstr "汇总角色名称" -#: users/serializers/user.py:84 users/serializers/user.py:253 +#: users/serializers/user.py:79 users/serializers/user.py:253 msgid "Is first login" msgstr "首次登录" -#: users/serializers/user.py:85 +#: users/serializers/user.py:80 msgid "Is valid" msgstr "账户是否有效" -#: users/serializers/user.py:86 +#: users/serializers/user.py:81 msgid "Is expired" msgstr " 是否过期" -#: users/serializers/user.py:87 +#: users/serializers/user.py:82 msgid "Avatar url" msgstr "头像路径" -#: users/serializers/user.py:91 +#: users/serializers/user.py:86 msgid "Groups name" msgstr "用户组名" -#: users/serializers/user.py:92 +#: users/serializers/user.py:87 msgid "Source name" msgstr "用户来源名" -#: users/serializers/user.py:94 +#: users/serializers/user.py:89 msgid "Super role name" msgstr "超级角色名称" diff --git a/apps/orgs/migrations/0008_auto_20200819_1732.py b/apps/orgs/migrations/0008_auto_20200819_1732.py new file mode 100644 index 000000000..75a773626 --- /dev/null +++ b/apps/orgs/migrations/0008_auto_20200819_1732.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.13 on 2020-08-19 09:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orgs', '0007_auto_20200728_1805'), + ] + + operations = [ + migrations.AlterField( + model_name='organizationmember', + name='role', + field=models.CharField(choices=[('Admin', 'Organization administrator'), ('User', 'Organization User'), ('Auditor', 'Organization auditor')], default='User', max_length=16, verbose_name='Role'), + ), + ] diff --git a/apps/orgs/models.py b/apps/orgs/models.py index c72d1ae82..1769787a4 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -13,7 +13,7 @@ from common.db.models import ChoiceSet class ROLE(ChoiceSet): ADMIN = choices.ADMIN, _('Organization administrator') - USER = choices.USER, _('User') + USER = choices.USER, _('Organization User') AUDITOR = choices.AUDITOR, _("Organization auditor") @@ -229,15 +229,29 @@ def _none2list(*args): return ([] if v is None else v for v in args) +class UserRoleMapper(dict): + def __init__(self, container=set): + super().__init__() + self.users = container() + self.admins = container() + self.auditors = container() + + self[ROLE.USER] = self.users + self[ROLE.ADMIN] = self.admins + self[ROLE.AUDITOR] = self.auditors + + class OrgMemeberManager(models.Manager): def remove_users_by_role(self, org, users=None, admins=None, auditors=None): + from users.models import User + if not any((users, admins, auditors)): return users, admins, auditors = _none2list(users, admins, auditors) send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False, - model=Organization, pk_set=[*users, *admins, *auditors], using=self.db) + model=User, pk_set=[*users, *admins, *auditors], using=self.db) send(action="pre_remove") self.filter(org_id=org.id).filter( @@ -248,6 +262,8 @@ class OrgMemeberManager(models.Manager): send(action="post_remove") def add_users_by_role(self, org, users=None, admins=None, auditors=None): + from users.models import User + if not any((users, admins, auditors)): return users, admins, auditors = _none2list(users, admins, auditors) @@ -266,7 +282,7 @@ class OrgMemeberManager(models.Manager): 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=Organization, pk_set=[*users, *admins, *auditors], using=self.db) + model=User, pk_set=[*users, *admins, *auditors], using=self.db) send(action='pre_add') self.bulk_create(oms_add) @@ -278,24 +294,56 @@ class OrgMemeberManager(models.Manager): new_users = _convert_to_uuid_set(new_users) return (old_users - new_users), (new_users - old_users) + def set_user_roles(self, org, user, roles): + """ + 设置某个用户在某个组织里的角色 + """ + old_roles = set(self.filter(org_id=org.id, user=user).values_list('role', flat=True)) + new_roles = set(roles) + + roles_remove = old_roles - new_roles + roles_add = new_roles - old_roles + + to_remove = UserRoleMapper() + to_add = UserRoleMapper() + + for role in roles_remove: + if role in to_remove: + to_remove[role].add(user) + for role in roles_add: + if role in to_add: + to_add[role].add(user) + + self.remove_users_by_role( + org, + to_remove.users, + to_remove.admins, + to_remove.auditors + ) + + self.add_users_by_role( + org, + to_add.users, + to_add.admins, + to_add.auditors + ) + def set_users_by_role(self, org, users=None, admins=None, auditors=None): + """ + 给组织设置带角色的用户 + """ + oms = self.filter(org_id=org.id).values_list('role', 'user_id') - old_users, old_admins, old_auditors = set(), set(), set() - - mapper = { - ROLE.USER: old_users, - ROLE.ADMIN: old_admins, - ROLE.AUDITOR: old_auditors - } + old_mapper = UserRoleMapper() for role, user_id in oms: - if role in mapper: - mapper[role].add(user_id) + if role in old_mapper: + old_mapper[role].add(user_id) - users_remove, users_add = self._get_remove_add_set(users, old_users) - admins_remove, admins_add = self._get_remove_add_set(admins, old_admins) - auditors_remove, auditors_add = self._get_remove_add_set(auditors, old_auditors) + users_remove, users_add = self._get_remove_add_set(users, old_mapper.users) + admins_remove, admins_add = self._get_remove_add_set(admins, old_mapper.admins) + auditors_remove, auditors_add = self._get_remove_add_set(auditors, old_mapper.auditors) self.remove_users_by_role( org, diff --git a/apps/users/api/user.py b/apps/users/api/user.py index b1c039318..ae4550931 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -56,31 +56,25 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet): def perform_create(self, serializer): validated_data = serializer.validated_data - if isinstance(validated_data, list): - org_roles = [item.pop('org_role', None) for item in validated_data] - else: - org_roles = [validated_data.pop('org_role', None)] + # `org_roles` 先 `pop` + if isinstance(validated_data, list): + org_roles = [item.pop('org_roles', []) for item in validated_data] + else: + org_roles = [validated_data.pop('org_roles', [])] + + # 创建用户 users = serializer.save() if isinstance(users, User): users = [users] - if current_org and current_org.is_real(): - mapper = { - ORG_ROLE.USER: [], - ORG_ROLE.ADMIN: [], - ORG_ROLE.AUDITOR: [] - } - for user, role in zip(users, org_roles): - if role in mapper: - mapper[role].append(user) - else: - mapper[ORG_ROLE.USER].append(user) - OrganizationMember.objects.set_users_by_role( - current_org, users=mapper[ORG_ROLE.USER], - admins=mapper[ORG_ROLE.ADMIN], - auditors=mapper[ORG_ROLE.AUDITOR] - ) + # 只有真实存在的组织才真正关联用户 + if current_org and current_org.is_real(): + for user, roles in zip(users, org_roles): + if not roles: + # 当前组织创建的用户,至少是该组织的`User` + roles.append(ORG_ROLE.USER) + OrganizationMember.objects.set_user_roles(current_org, user, roles) self.send_created_signal(users) def get_permissions(self): @@ -101,6 +95,23 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet): self.check_object_permissions(self.request, obj) self.perform_destroy(obj) + def perform_update(self, serializer): + validated_data = serializer.validated_data + # `org_roles` 先 `pop` + if isinstance(validated_data, list): + org_roles = [item.pop('org_roles', None) for item in validated_data] + else: + org_roles = [validated_data.pop('org_roles', None)] + + users = serializer.save() + if isinstance(users, User): + users = [users] + if current_org and current_org.is_real(): + for user, roles in zip(users, org_roles): + if roles is not None: + # roles 是 `Node` 表明不需要更新 + OrganizationMember.objects.set_user_roles(current_org, user, roles) + def perform_bulk_update(self, serializer): # TODO: 需要测试 users_ids = [ diff --git a/apps/users/models/user.py b/apps/users/models/user.py index b496f17bd..7a4dc2a4f 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -154,7 +154,7 @@ class AuthMixin: class RoleMixin: class ROLE(ChoiceSet): ADMIN = choices.ADMIN, _('System administrator') - USER = choices.USER, _('User') + USER = choices.USER, _('System User') AUDITOR = choices.AUDITOR, _('System auditor') APP = 'App', _('Application') @@ -164,15 +164,15 @@ class RoleMixin: def role_display(self): return self.get_role_display() - @property - def org_role_display(self): + @lazyproperty + def org_roles(self): from orgs.models import ROLE as ORG_ROLE if not current_org.is_real(): if self.is_superuser: - return ORG_ROLE.ADMIN.label + return [ORG_ROLE.ADMIN] else: - return ORG_ROLE.USER.label + return [ORG_ROLE.USER] if hasattr(self, 'gc_m2m_org_members__role'): names = self.gc_m2m_org_members__role @@ -184,8 +184,14 @@ class RoleMixin: roles = set(self.m2m_org_members.filter( org_id=current_org.id ).values_list('role', flat=True)) + roles = list(roles) + roles.sort() + return roles - return ' | '.join([str(ORG_ROLE[role]) for role in roles if role in ORG_ROLE]) + @lazyproperty + def org_role_display(self): + from orgs.models import ROLE as ORG_ROLE + return ' | '.join([str(ORG_ROLE[role]) for role in self.org_roles if role in ORG_ROLE]) def current_org_roles(self): from orgs.models import OrganizationMember, ROLE as ORG_ROLE diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index e246ba203..6d8caaf8a 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -7,7 +7,6 @@ from rest_framework import serializers from common.utils import validate_ssh_public_key from common.mixins import CommonBulkSerializerMixin -from common.serializers import AdaptedBulkListSerializer from common.permissions import CanUpdateDeleteUser from common.drf.fields import GroupConcatedPrimaryKeyRelatedField from orgs.models import ROLE as ORG_ROLE @@ -51,17 +50,13 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer): login_blocked = serializers.SerializerMethodField() can_update = serializers.SerializerMethodField() can_delete = serializers.SerializerMethodField() - org_role = serializers.ChoiceField( - label=_('Organization role name'), write_only=True, - allow_null=True, required=False, allow_blank=True, - choices=ORG_ROLE.choices - ) + org_roles = serializers.ListField(label=_('Organization role name'), allow_null=True, required=False, + child=serializers.ChoiceField(choices=ORG_ROLE.choices)) total_role_display = serializers.SerializerMethodField(label=_('Total role name')) key_prefix_block = "_LOGIN_BLOCK_{}" class Meta: model = User - list_serializer_class = AdaptedBulkListSerializer # mini 是指能识别对象的最小单元 fields_mini = ['id', 'name', 'username'] # small 指的是 不需要计算的直接能从一张表中获取到的数据 @@ -75,7 +70,7 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer): ] fields = fields_small + [ 'groups', 'role', 'groups_display', 'role_display', - 'can_update', 'can_delete', 'login_blocked', 'org_role' + 'can_update', 'can_delete', 'login_blocked', 'org_roles' ] extra_kwargs = { From fcd8356e9030fb39a1d625dd3d2a7974d46139bd Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 19 Aug 2020 17:59:25 +0800 Subject: [PATCH 09/14] =?UTF-8?q?fix(users):=20=E7=BB=84=E7=BB=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=98=EF=BC=8C=E7=A7=BB=E9=99=A4=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E6=88=90=E5=91=98=E6=8A=A5=E9=94=99500=20#231?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.po | 81 +++++++++---------- .../migrations/0008_auto_20200819_1732.py | 18 ----- apps/orgs/models.py | 17 +++- apps/users/models/user.py | 24 +++--- apps/users/serializers/user.py | 10 +-- 5 files changed, 72 insertions(+), 78 deletions(-) delete mode 100644 apps/orgs/migrations/0008_auto_20200819_1732.py diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 66fb19a2a..f0a2309ef 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-19 17:34+0800\n" +"POT-Creation-Date: 2020-08-19 18:10+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -29,7 +29,7 @@ msgstr "自定义" #: orgs/models.py:22 perms/models/base.py:48 settings/models.py:27 #: terminal/models.py:27 terminal/models.py:344 terminal/models.py:376 #: terminal/models.py:413 users/forms/profile.py:20 users/models/group.py:15 -#: users/models/user.py:495 users/templates/users/_select_user_modal.html:13 +#: users/models/user.py:491 users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_asset_permission.html:37 #: users/templates/users/user_asset_permission.html:154 #: users/templates/users/user_database_app_permission.html:36 @@ -79,7 +79,7 @@ msgstr "数据库" #: assets/models/label.py:23 ops/models/adhoc.py:37 orgs/models.py:25 #: perms/models/base.py:56 settings/models.py:32 terminal/models.py:37 #: terminal/models.py:383 terminal/models.py:420 users/models/group.py:16 -#: users/models/user.py:528 users/templates/users/user_detail.html:115 +#: users/models/user.py:524 users/templates/users/user_detail.html:115 #: users/templates/users/user_granted_database_app.html:38 #: users/templates/users/user_granted_remote_app.html:37 #: users/templates/users/user_group_detail.html:62 @@ -146,8 +146,8 @@ msgstr "参数" #: assets/models/base.py:240 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:23 orgs/models.py:374 perms/models/base.py:54 -#: users/models/user.py:536 users/serializers/group.py:35 +#: orgs/models.py:23 orgs/models.py:389 perms/models/base.py:54 +#: users/models/user.py:532 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:56 #: xpack/plugins/cloud/models.py:146 xpack/plugins/gathered_user/models.py:30 @@ -161,7 +161,7 @@ msgstr "创建者" #: assets/models/domain.py:23 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:27 -#: orgs/models.py:24 orgs/models.py:372 perms/models/base.py:55 +#: orgs/models.py:24 orgs/models.py:387 perms/models/base.py:55 #: users/models/group.py:18 users/templates/users/user_group_detail.html:58 #: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:149 msgid "Date created" @@ -354,7 +354,7 @@ msgstr "" #: audits/models.py:99 authentication/forms.py:11 #: authentication/templates/authentication/login.html:21 #: authentication/templates/authentication/xpack_login.html:101 -#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:493 +#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:489 #: users/templates/users/_select_user_modal.html:14 #: users/templates/users/user_detail.html:53 #: users/templates/users/user_list.html:15 @@ -395,7 +395,7 @@ msgstr "SSH公钥" #: assets/models/base.py:239 assets/models/gathered_user.py:20 #: common/db/models.py:70 common/mixins/models.py:51 ops/models/adhoc.py:39 -#: orgs/models.py:373 +#: orgs/models.py:388 msgid "Date updated" msgstr "更新日期" @@ -407,7 +407,7 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 users/models/user.py:514 +#: assets/models/cluster.py:22 users/models/user.py:510 #: users/templates/users/user_detail.html:62 msgid "Phone" msgstr "手机" @@ -433,7 +433,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:14 -#: users/models/user.py:661 +#: users/models/user.py:657 msgid "System" msgstr "系统" @@ -547,7 +547,7 @@ msgstr "默认资产组" #: assets/models/label.py:15 audits/models.py:36 audits/models.py:56 #: audits/models.py:69 audits/serializers.py:77 authentication/models.py:46 -#: authentication/models.py:90 orgs/models.py:370 +#: authentication/models.py:90 orgs/models.py:16 orgs/models.py:385 #: perms/forms/asset_permission.py:83 perms/forms/database_app_permission.py:38 #: perms/forms/remote_app_permission.py:40 perms/models/base.py:49 #: templates/index.html:78 terminal/backends/command/models.py:18 @@ -555,7 +555,8 @@ msgstr "默认资产组" #: tickets/models/ticket.py:30 tickets/models/ticket.py:137 #: tickets/serializers/request_asset_perm.py:65 #: tickets/serializers/ticket.py:31 users/forms/group.py:15 -#: users/models/user.py:649 users/serializers/group.py:20 +#: users/models/user.py:158 users/models/user.py:645 +#: users/serializers/group.py:20 #: users/templates/users/user_asset_permission.html:38 #: users/templates/users/user_asset_permission.html:64 #: users/templates/users/user_database_app_permission.html:37 @@ -732,14 +733,14 @@ msgid "Backend" msgstr "后端" #: assets/serializers/asset_user.py:75 users/forms/profile.py:148 -#: users/models/user.py:525 users/templates/users/user_password_update.html:48 +#: users/models/user.py:521 users/templates/users/user_password_update.html:48 #: users/templates/users/user_profile.html:69 #: users/templates/users/user_profile_update.html:46 #: users/templates/users/user_pubkey_update.html:46 msgid "Public key" msgstr "SSH公钥" -#: assets/serializers/asset_user.py:79 users/models/user.py:522 +#: assets/serializers/asset_user.py:79 users/models/user.py:518 msgid "Private key" msgstr "ssh私钥" @@ -1024,7 +1025,7 @@ msgstr "Agent" #: audits/models.py:104 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: authentication/templates/authentication/login_otp.html:6 -#: users/forms/profile.py:52 users/models/user.py:517 +#: users/forms/profile.py:52 users/models/user.py:513 #: users/serializers/user.py:240 users/templates/users/user_detail.html:77 #: users/templates/users/user_profile.html:87 msgid "MFA" @@ -1264,7 +1265,7 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:415 users/serializers/user.py:237 +#: users/models/user.py:411 users/serializers/user.py:237 #: users/templates/users/user_profile.html:94 #: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:166 @@ -1273,7 +1274,7 @@ msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:416 users/serializers/user.py:238 +#: users/models/user.py:412 users/serializers/user.py:238 #: users/templates/users/user_profile.html:92 #: users/templates/users/user_profile.html:170 msgid "Enable" @@ -1684,7 +1685,7 @@ msgid "The current organization cannot be deleted" msgstr "" #: orgs/mixins/models.py:56 orgs/mixins/serializers.py:25 orgs/models.py:40 -#: orgs/models.py:369 +#: orgs/models.py:384 msgid "Organization" msgstr "组织" @@ -1692,15 +1693,11 @@ msgstr "组织" msgid "Organization administrator" msgstr "组织管理员" -#: orgs/models.py:16 -msgid "Organization User" -msgstr "组织用户" - #: orgs/models.py:17 msgid "Organization auditor" msgstr "组织审计员" -#: orgs/models.py:371 users/forms/user.py:27 users/models/user.py:505 +#: orgs/models.py:386 users/forms/user.py:27 users/models/user.py:501 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:73 #: users/templates/users/user_list.html:16 @@ -1725,7 +1722,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件" #: perms/forms/asset_permission.py:86 perms/forms/database_app_permission.py:41 #: perms/forms/remote_app_permission.py:43 perms/models/base.py:50 #: templates/_nav.html:21 users/forms/user.py:168 users/models/group.py:31 -#: users/models/user.py:501 users/serializers/user.py:48 +#: users/models/user.py:497 users/serializers/user.py:48 #: users/templates/users/_select_user_modal.html:16 #: users/templates/users/user_asset_permission.html:39 #: users/templates/users/user_asset_permission.html:67 @@ -1792,7 +1789,7 @@ msgid "Asset permission" msgstr "资产授权" #: perms/models/base.py:53 tickets/serializers/request_asset_perm.py:31 -#: users/models/user.py:533 users/templates/users/user_detail.html:93 +#: users/models/user.py:529 users/templates/users/user_detail.html:93 #: users/templates/users/user_profile.html:120 msgid "Date expired" msgstr "失效日期" @@ -2799,7 +2796,7 @@ msgstr "确认密码" msgid "Password does not match" msgstr "密码不一致" -#: users/forms/profile.py:89 users/models/user.py:497 +#: users/forms/profile.py:89 users/models/user.py:493 #: users/templates/users/user_detail.html:57 #: users/templates/users/user_profile.html:59 msgid "Email" @@ -2840,7 +2837,7 @@ msgstr "不能和原来的密钥相同" msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/user.py:31 users/models/user.py:540 +#: users/forms/user.py:31 users/models/user.py:536 #: users/templates/users/user_detail.html:89 #: users/templates/users/user_list.html:18 #: users/templates/users/user_profile.html:102 @@ -2874,47 +2871,43 @@ msgstr "设置密码" msgid "Password strategy" msgstr "密码策略" -#: users/models/user.py:156 +#: users/models/user.py:157 msgid "System administrator" msgstr "系统管理员" -#: users/models/user.py:157 -msgid "System User" -msgstr "系统用户" - -#: users/models/user.py:158 +#: users/models/user.py:159 msgid "System auditor" msgstr "系统审计员" -#: users/models/user.py:159 +#: users/models/user.py:160 msgid "Application" msgstr "应用程序" -#: users/models/user.py:417 users/templates/users/user_profile.html:90 +#: users/models/user.py:413 users/templates/users/user_profile.html:90 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:484 +#: users/models/user.py:480 msgid "Local" msgstr "数据库" -#: users/models/user.py:508 +#: users/models/user.py:504 msgid "Avatar" msgstr "头像" -#: users/models/user.py:511 users/templates/users/user_detail.html:68 +#: users/models/user.py:507 users/templates/users/user_detail.html:68 msgid "Wechat" msgstr "微信" -#: users/models/user.py:544 +#: users/models/user.py:540 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:657 +#: users/models/user.py:653 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:660 +#: users/models/user.py:656 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -4088,6 +4081,12 @@ msgstr "企业版" msgid "Ultimate edition" msgstr "旗舰版" +#~ msgid "Organization User" +#~ msgstr "组织用户" + +#~ msgid "System User" +#~ msgstr "系统用户" + #~ msgid "Auditor" #~ msgstr "审计员" diff --git a/apps/orgs/migrations/0008_auto_20200819_1732.py b/apps/orgs/migrations/0008_auto_20200819_1732.py deleted file mode 100644 index 75a773626..000000000 --- a/apps/orgs/migrations/0008_auto_20200819_1732.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.13 on 2020-08-19 09:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('orgs', '0007_auto_20200728_1805'), - ] - - operations = [ - migrations.AlterField( - model_name='organizationmember', - name='role', - field=models.CharField(choices=[('Admin', 'Organization administrator'), ('User', 'Organization User'), ('Auditor', 'Organization auditor')], default='User', max_length=16, verbose_name='Role'), - ), - ] diff --git a/apps/orgs/models.py b/apps/orgs/models.py index 1769787a4..2c47ed756 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -13,7 +13,7 @@ from common.db.models import ChoiceSet class ROLE(ChoiceSet): ADMIN = choices.ADMIN, _('Organization administrator') - USER = choices.USER, _('Organization User') + USER = choices.USER, _('User') AUDITOR = choices.AUDITOR, _("Organization auditor") @@ -243,6 +243,21 @@ class UserRoleMapper(dict): class OrgMemeberManager(models.Manager): + def remove_users(self, org, users): + from users.models import User + pk_set = [] + for user in users: + if hasattr(user, 'pk'): + pk_set.append(user.pk) + else: + pk_set.append(user) + + 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_remove") + self.filter(org_id=org.id, user_id__in=pk_set).delete() + send(action="post_remove") + def remove_users_by_role(self, org, users=None, admins=None, auditors=None): from users.models import User diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 7a4dc2a4f..310084d06 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -18,6 +18,7 @@ from django.shortcuts import reverse from common.local import LOCAL_DYNAMIC_SETTINGS from orgs.utils import current_org +from orgs.models import OrganizationMember from common.utils import date_expired_default, get_logger, lazyproperty from common import fields from common.const import choices @@ -154,7 +155,7 @@ class AuthMixin: class RoleMixin: class ROLE(ChoiceSet): ADMIN = choices.ADMIN, _('System administrator') - USER = choices.USER, _('System User') + USER = choices.USER, _('User') AUDITOR = choices.AUDITOR, _('System auditor') APP = 'App', _('Application') @@ -189,9 +190,19 @@ class RoleMixin: return roles @lazyproperty - def org_role_display(self): + def org_roles_label_list(self): from orgs.models import ROLE as ORG_ROLE - return ' | '.join([str(ORG_ROLE[role]) for role in self.org_roles if role in ORG_ROLE]) + return [str(ORG_ROLE[role]) for role in self.org_roles if role in ORG_ROLE] + + @lazyproperty + def org_role_display(self): + return ' | '.join(self.org_roles_label_list) + + @lazyproperty + def total_role_display(self): + roles = list({self.role_display, *self.org_roles_label_list}) + roles.sort() + return ' | '.join(roles) def current_org_roles(self): from orgs.models import OrganizationMember, ROLE as ORG_ROLE @@ -320,12 +331,7 @@ class RoleMixin: def remove(self): if not current_org.is_real(): return - if self.can_user_current_org: - current_org.users.remove(self) - if self.can_admin_current_org: - current_org.admins.remove(self) - if self.can_audit_current_org: - current_org.auditors.remove(self) + OrganizationMember.objects.remove_users(current_org, [self]) class TokenMixin: diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index 6d8caaf8a..d6968a2d8 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -52,7 +52,6 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer): can_delete = serializers.SerializerMethodField() org_roles = serializers.ListField(label=_('Organization role name'), allow_null=True, required=False, child=serializers.ChoiceField(choices=ORG_ROLE.choices)) - total_role_display = serializers.SerializerMethodField(label=_('Total role name')) key_prefix_block = "_LOGIN_BLOCK_{}" class Meta: @@ -87,6 +86,7 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer): 'source_display': {'label': _('Source name')}, 'org_role_display': {'label': _('Organization role name')}, 'role_display': {'label': _('Super role name')}, + 'total_role_display': {'label': _('Total role name')} } def __init__(self, *args, **kwargs): @@ -105,14 +105,6 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer): choices.pop(User.ROLE.AUDITOR, None) role._choices = choices - def get_total_role_display(self, instance): - role_display = instance.role_display - org_role_display = instance.org_role_display - if role_display == org_role_display: - return role_display - else: - return f'{role_display} | {org_role_display}' - def validate_role(self, value): request = self.context.get('request') if not request.user.is_superuser and value != User.ROLE.USER: From 9ca8ab218ccb582fee2596b2d3137d739e4840a1 Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 19 Aug 2020 20:26:16 +0800 Subject: [PATCH 10/14] =?UTF-8?q?fix(authentication):=20=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=A4=8D=E6=A0=B8Not=20found?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/login_confirm.py | 10 ---------- apps/authentication/mixins.py | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/apps/authentication/api/login_confirm.py b/apps/authentication/api/login_confirm.py index 986488f2b..4fbf3d5d3 100644 --- a/apps/authentication/api/login_confirm.py +++ b/apps/authentication/api/login_confirm.py @@ -34,16 +34,6 @@ class LoginConfirmSettingUpdateApi(UpdateAPIView): class TicketStatusApi(mixins.AuthMixin, APIView): permission_classes = () - def get_ticket(self): - from tickets.models import Ticket - ticket_id = self.request.session.get("auth_ticket_id") - logger.debug('Login confirm ticket id: {}'.format(ticket_id)) - if not ticket_id: - ticket = None - else: - ticket = get_object_or_none(Ticket, pk=ticket_id) - return ticket - def get(self, request, *args, **kwargs): try: self.check_user_login_confirm() diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index e2da6b8e5..8d6b7765a 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -170,7 +170,7 @@ class AuthMixin: if not ticket_id: ticket = None else: - ticket = get_object_or_none(Ticket, pk=ticket_id) + ticket = Ticket.origin_objects.get(pk=ticket_id) return ticket def get_ticket_or_create(self, confirm_setting): From 0fd2f18240fff7dc249722f022749f69bcb09d09 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 19 Aug 2020 21:22:28 +0800 Subject: [PATCH 11/14] =?UTF-8?q?fix(authentication):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=97=B6=E6=9C=89=E6=97=B6=E8=A7=A3=E5=AF=86?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/utils.py | 8 +++++++- apps/jumpserver/views/other.py | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/authentication/utils.py b/apps/authentication/utils.py index cb697c237..f6750a73d 100644 --- a/apps/authentication/utils.py +++ b/apps/authentication/utils.py @@ -34,7 +34,13 @@ def rsa_decrypt(cipher_text, rsa_private_key=None): if rsa_private_key is None: # rsa_private_key 为 None,可以能是API请求认证,不需要解密 return cipher_text + key = RSA.importKey(rsa_private_key) cipher = PKCS1_v1_5.new(key) - message = cipher.decrypt(base64.b64decode(cipher_text.encode()), 'error').decode() + cipher_decoded = base64.b64decode(cipher_text.encode()) + # Todo: 弄明白为何要以下这么写,https://xbuba.com/questions/57035263 + if len(cipher_decoded) == 127: + hex_fixed = '00' + cipher_decoded.hex() + cipher_decoded = base64.b16decode(hex_fixed.upper()) + message = cipher.decrypt(cipher_decoded, b'error').decode() return message diff --git a/apps/jumpserver/views/other.py b/apps/jumpserver/views/other.py index a0b386383..134d599a6 100644 --- a/apps/jumpserver/views/other.py +++ b/apps/jumpserver/views/other.py @@ -52,6 +52,7 @@ def redirect_format_api(request, *args, **kwargs): return JsonResponse({"msg": "Redirect url failed: {}".format(_path)}, status=404) +@csrf_exempt def redirect_old_apps_view(request, *args, **kwargs): path = request.get_full_path() if path.find('/core') != -1: From 9cb59859478f8db5afd9650a87fc9d3d9b71d182 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 19 Aug 2020 21:44:20 +0800 Subject: [PATCH 12/14] =?UTF-8?q?fix(orgs):=20=E5=88=9B=E5=BB=BA=E7=BB=84?= =?UTF-8?q?=E7=BB=87=E7=94=A8=E6=88=B7=E4=B8=8D=E5=BF=85=E5=A1=AB=20(#4515?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(role): 更改role的顺序 * fix(tickets): 修复工单邮件跳转地址 * fix(tickets): 修复工单复制链接地址不对 * fix(orgs): 创建组织用户不必填 Co-authored-by: xinwen --- apps/authentication/views/login.py | 3 ++- apps/common/const/front_urls.py | 2 ++ .../orgs/migrations/0008_auto_20200819_2041.py | 18 ++++++++++++++++++ apps/orgs/models.py | 2 +- apps/orgs/serializers.py | 6 +++--- apps/tickets/utils.py | 7 ++----- .../migrations/0030_auto_20200819_2041.py | 18 ++++++++++++++++++ apps/users/models/user.py | 2 +- 8 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 apps/common/const/front_urls.py create mode 100644 apps/orgs/migrations/0008_auto_20200819_2041.py create mode 100644 apps/users/migrations/0030_auto_20200819_2041.py diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 5493ac3c7..506a8cc87 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -18,6 +18,7 @@ from django.views.generic.edit import FormView from django.conf import settings from django.urls import reverse_lazy +from common.const.front_urls import TICKET_DETAIL from common.utils import get_request_ip, get_object_or_none from users.utils import ( redirect_user_first_login_or_index @@ -185,7 +186,7 @@ class UserLoginWaitConfirmView(TemplateView): context = super().get_context_data(**kwargs) if ticket: timestamp_created = datetime.datetime.timestamp(ticket.date_created) - ticket_detail_url = reverse('tickets:ticket-detail', kwargs={'pk': ticket_id}) + ticket_detail_url = TICKET_DETAIL.format(id=ticket_id) msg = _("""Wait for {} confirm, You also can copy link to her/him
Don't close this page""").format(ticket.assignees_display) else: diff --git a/apps/common/const/front_urls.py b/apps/common/const/front_urls.py new file mode 100644 index 000000000..12d47ed17 --- /dev/null +++ b/apps/common/const/front_urls.py @@ -0,0 +1,2 @@ + +TICKET_DETAIL = '/ui/#/tickets/tickets/{id}' diff --git a/apps/orgs/migrations/0008_auto_20200819_2041.py b/apps/orgs/migrations/0008_auto_20200819_2041.py new file mode 100644 index 000000000..524d29e39 --- /dev/null +++ b/apps/orgs/migrations/0008_auto_20200819_2041.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.13 on 2020-08-19 12:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orgs', '0007_auto_20200728_1805'), + ] + + operations = [ + migrations.AlterField( + model_name='organizationmember', + name='role', + field=models.CharField(choices=[('Admin', 'Organization administrator'), ('Auditor', 'Organization auditor'), ('User', 'User')], default='User', max_length=16, verbose_name='Role'), + ), + ] diff --git a/apps/orgs/models.py b/apps/orgs/models.py index 2c47ed756..f76d88e2a 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -13,8 +13,8 @@ from common.db.models import ChoiceSet class ROLE(ChoiceSet): ADMIN = choices.ADMIN, _('Organization administrator') - USER = choices.USER, _('User') AUDITOR = choices.AUDITOR, _("Organization auditor") + USER = choices.USER, _('User') class Organization(models.Model): diff --git a/apps/orgs/serializers.py b/apps/orgs/serializers.py index d7e8ae2d1..6d52fdc23 100644 --- a/apps/orgs/serializers.py +++ b/apps/orgs/serializers.py @@ -10,9 +10,9 @@ from .models import Organization, OrganizationMember, ROLE as ORG_ROLE class OrgSerializer(ModelSerializer): - users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True) - admins = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True) - auditors = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True) + users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False) + admins = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False) + auditors = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False) class Meta: model = Organization diff --git a/apps/tickets/utils.py b/apps/tickets/utils.py index 152b5182b..f3d6ee91e 100644 --- a/apps/tickets/utils.py +++ b/apps/tickets/utils.py @@ -4,6 +4,7 @@ from urllib.parse import urljoin from django.conf import settings from django.utils.translation import ugettext as _ +from common.const.front_urls import TICKET_DETAIL from common.utils import get_logger from common.tasks import send_mail_async @@ -20,11 +21,7 @@ def send_new_ticket_mail_to_assignees(ticket: Ticket, assignees): subject = '{}: {}'.format(_("New ticket"), ticket.title) # 这里要设置前端地址,因为要直接跳转到页面 - if ticket.type == ticket.TYPE.REQUEST_ASSET_PERM: - detail_url = urljoin(settings.SITE_URL, f'/tickets/tickets/request-asset-perm/{ticket.id}') - else: - detail_url = urljoin(settings.SITE_URL, f'/tickets/tickets/{ticket.id}') - + detail_url = urljoin(settings.SITE_URL, TICKET_DETAIL.format(id=ticket.id)) message = _("""

Your has a new ticket

diff --git a/apps/users/migrations/0030_auto_20200819_2041.py b/apps/users/migrations/0030_auto_20200819_2041.py new file mode 100644 index 000000000..775d38ac5 --- /dev/null +++ b/apps/users/migrations/0030_auto_20200819_2041.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.13 on 2020-08-19 12:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0029_auto_20200814_1650'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='role', + field=models.CharField(blank=True, choices=[('Admin', 'System administrator'), ('Auditor', 'System auditor'), ('User', 'User'), ('App', 'Application')], default='User', max_length=10, verbose_name='Role'), + ), + ] diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 310084d06..f65b9398e 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -155,8 +155,8 @@ class AuthMixin: class RoleMixin: class ROLE(ChoiceSet): ADMIN = choices.ADMIN, _('System administrator') - USER = choices.USER, _('User') AUDITOR = choices.AUDITOR, _('System auditor') + USER = choices.USER, _('User') APP = 'App', _('Application') role = ROLE.USER From 072c3155caca89ec3fe1c825893589f61ccdf8ac Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 19 Aug 2020 21:50:25 +0800 Subject: [PATCH 13/14] =?UTF-8?q?style(orgs):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=BB=84=E7=BB=87=E5=88=A0=E9=99=A4=E5=A4=B1=E8=B4=A5=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | Bin 56869 -> 56957 bytes apps/locale/zh/LC_MESSAGES/django.po | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 080d1a348e1567a19afadd49545a448086844351..e0e23c4e4e75e48efbe1062411e431adbaca42a0 100644 GIT binary patch delta 9459 zcmXZg34D!b+Q;#8s6>c8_9Y>ReZ(FSl%f^JHdVE>h#+)g$wEVOY6(GTIELy_TT4+H z-V~+wy)?DgTFcvsrD}Oit3{3X_jh0O`Izr>UH5j~_p_YLe7>{b^PL4ZO)l=M3p7qJB1#me~1ET7`>R3vVSf!H0jz?VYd?wQ$X~=N z_z;7zm~VocpgbxOhC1qc*aVxSPG%(b#x$&hc^Hb1F&N8DbVnPGnx_fs*0n(u+7Wd^ zv6k;;d0#&+w7_6%7?0Y~`>1<06}6-JsEL=M`meTo4d(^EvhdQZz%l~1%K+PBM zzB}Qv$jSOVjkr){15pbMMNRx3M&cCIz+BW3?#Bjr5vyWAn#FO3h<-u?Gn_(^(kuLzfecK6*cgHnTJ~FJZj;5)Ct^275ra|i%oL# zR5fd&POv_fV0}*v7ZtHP>fVpQdN>}ngJq}!zCjgu6f0mJzJ@okGS;8$UdJw|!rn#Q z`@f?KO2JAv9d)wH(Wi<}S%({_ojgPhe1clo`+;ukUJ3dQ71FxYbylL@W^AWx*2sNQM-Nlu#4si|CxLDMNdRqHn z)Jcp)J(Q`a6PSg%m7k?^{#rPPgxrF9_;#X>>=bH(>!=;xM@{e)buwicuZ3%%;t13U zHA5{BV{sR2?}^&*Ak+zt^l_mD5>W#uV+3Yk7;ZGbNA3IpR>8s_x@V&n>S#M+bNn0D zz}cvi*oZozU05A&qUJ9=g(A?`n2Q81=3xi?1NHDlPxW{v;&2SbYd8>JU~L>Y%@r^W z8xyZYzSf@8SRcDgcMoF{<`B=p_1HSYJ?#0&N%}l5xKKrvX1IwvU`yfzjKY-|h?h}E z{}bxO{%Z!$Xrn}as@R*P57F{eKCx9IO^e@iQ3Ro)Pi53=HH82_&Dm>IE_Aa_+Ku9@IH>l zKTtazHrq{*fGRA-;yD&CMisgdi{MeLk7rOHm=~z|Bj>ocDF#(|H&lV|%;Eg2a*;|x z6D+h2%dsf&W>lrSP)DAJD)cJW$J-c=Az3cp3e~Rz7RElPg$A0*sJA2wb+Y@jIDb79 zH%PR@Td08%b6vhM>d0E43W-78>+V<--$WHO1V`g&tcmAQADll?Cs|^idp1JM>e!Zi zBOe!9;P0rXJsBHe4pzq>aWlR^?R4`;?jE1R*2F)fZb7YVcZ=Gi7VL{vygZbs4Njcz z3Qb3CFcUS-H=m1=TzrYz;V#r`lZVCe25P4dQ9Jz;HDSO4cQ1oc{i>k~ZfN-)Sd#*V zV0ZE}{Ce5>)Z;Mx+d>}N0;INcagxNPMf{n?fDMa1p5KWJF5!<5e1?2;JSm@09R7iO z=(qKse1S3+7(tW62M;+l948z%|_6^txzr~|iVikY9;04t8C2O_&fUTA~lFeM`7VNQx zlNd~V)x2x|fqGU7edgMO&5EdeZL=ZjRyDKswq`8qH>97%6XYq#S6?O<_c?H zYkq}VU?1v)j$<8sjQuh6bN4W(q56G=ns+xw;34zg=iGlalwIR?R0Z{3)c6SZuU!9BJNa)DRuXQ`AiHf_M{ZWOD zuy}^)xBLp!gnLj6d~59&u{7~bEQn7m{~QB|i}=293zb4WOx3VHMqv$%$3~cmdPcUP z1|CHfc*4AbD)5fA7hdQ3l|c0?XNFt8vFU4R7j3OWCyRTdDt_DYgUnH=PwE8Jf*+v@ z%CYt>sMl*hYUdYF--o;AQ!{A&%e>E1feYQM2(!6s@N~o=^1aQ0=5TYYnT9H0I=06- z7Jp~$XHd7~N6X(soyf0PTAw8E23Ki_8G%~3HP*z}PzxlOV^Ocs1T)+6>rmr1nLAJm z9kljS7GJ<3*C2n$|d-ei#Ps?s}6E{QcDB9vq7I(9_F9y>-$Q*?c#1pU!E=Lu33sum4 z)O_9?H?C+7=dVgCkWfP%vk5BS#yWOGP4u?44@MO_%Hmn5{(jVkR--nw6E*HMs?dw( zP4hoFoWB}8|8f>XRa(hxX!-W2iMv}o+)Oe*L@hMe;)NEk#A4*Xz!JC>wccU#jL$Bv zqdr9U%o3a2|M3usn)qGhhtMltncF#k z4fu#eJzRt;Xdf2FvlxOuqWV2TJ>5@G3kL3RhM3h+;~Sx#`d2Lb0 z4d={js7mjkcKFbIjv7#8r@QA>QSA+}4z{*94zD&}9z4yd=~O*3A3ABhw$v|y(B57fe| zP&;0STJWrS)x2#!LVald#A;Z5uPdZIYMwr3e{%@xL`R}eM?T3CS*V9;Id;YEsKQ>^ z=PYGbGwY)!YK7Wati=N?PDG7QH|Jn=;*U`q+Od!Gugk?j63y^Fs^akd&U&Z`B2fd{ zTK;v^t?FU%XjH%P7W+^I%`g|CPG$|N;6tc&F6{TYi))s+iJIu1b$p7NsMrCwU{zEc zg?jkjK<#KKYTR_o&$oCzs{b~NkDv-aYd-XGp^1VIxH8YTSC% zJ>F&Q7g6(GM-}=Q^}z}_N+GQ8iQz&6yQ1FrL8xaU6Ls%aq6#^Hs{9yg!t<8DXa0&R z>=~+Gp>Lg~QR72V>(sV<6u$iZ?`(-as9(3is7ezt2-D3>a{=lhT8f%@lerDG@NV-s zYQYQEeiyZ|M`qDuy#E?d^O&0`64jvKLT?=a6`5cywF3qMAUe~v1s$Z_{2tcJ>W zG<%wZP$xIa;t!A8`#;wb3$YL#R-zVIi$yTU+P9-l<}j+zA5aB7K;5b**bXay=L#5r znrEar1`86WTAb#yiz%p2Z>GhoP?fI3S8xyNC=a3vIFG@2%ksaX3JT0~`7l&r4N(QO zM2%}}{?*!j?{J}sMq(9Av3LP$;Bt%Cn7QU&Gtaza-a?K06;;49%NP3I{hd%6Ymkq2 z`aJJ&p~|PA3it#o;WpHVG;3Jmrq63x*NDg$;2c>K?B_6?_yI;0;W|u?2EL8t&48he{ z3cogwp>FjRRKGj;^8Npl3r!q!&fT+;sJN0@9W|hiwMSdN6P6_3&GLiH5!OBq%hEm( zwXu2Tr>G6&oa6j;?~apDW%tczX5k;)QI|!vH$ok0w8gzq581mGCzz?`G}K12u@5e` z_^$cr2hLwRdt!;;^DZufnz%OVd(as55VbJJpbGi|wX-d#c@AM2ykPM!sCizX`UPEZ zaYa-d?z4-gs0llw9=4vShb`UmE6k0k33s3tJct$WJhs7ySQQ&ybo0exN8)~{lUR!S z0If#N<6Fyx7T#^2sOt6AUEhYS5&{*KyFnJez^^xCL=e^fpdHQ_8Q=jEQF3RwH2D`11U1=W9#c^Xw% zKI+IHp$aZ=Rr#j;7Z*xYL`@iux`z>{0nu0uV^I^2@+U-&@J{iciVQDyfZwfzgSEk?_~dwmx;&&tTp-f(m2`uxllcb0s1>yzoXw@kmeBRhA} t{wYOrgYv=)R?gq$&!08-);H5{eKYglU(dZgZP)Ep8(jC?`X`?j`+tAWOSb?3 delta 9360 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 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index f0a2309ef..ec532c0b6 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -1678,11 +1678,11 @@ msgstr "磁盘使用率超过 80%: {} => {}" #: orgs/api.py:58 msgid "Organization contains undeleted resources" -msgstr "" +msgstr "组织包含未删除的资源" #: orgs/api.py:62 msgid "The current organization cannot be deleted" -msgstr "" +msgstr "当前组织不能被删除" #: orgs/mixins/models.py:56 orgs/mixins/serializers.py:25 orgs/models.py:40 #: orgs/models.py:384 From 15992ad5b322d3183f5fd6e449b27141255668ac Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 19 Aug 2020 21:47:58 +0800 Subject: [PATCH 14/14] =?UTF-8?q?fix(tickets):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=B7=A5=E5=8D=95comment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/orgs/serializers.py | 2 +- apps/tickets/api/request_asset_perm.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/orgs/serializers.py b/apps/orgs/serializers.py index 6d52fdc23..c36d67d89 100644 --- a/apps/orgs/serializers.py +++ b/apps/orgs/serializers.py @@ -6,7 +6,7 @@ from users.models.user import User from common.serializers import AdaptedBulkListSerializer from common.drf.serializers import BulkModelSerializer from common.db.models import concated_display as display -from .models import Organization, OrganizationMember, ROLE as ORG_ROLE +from .models import Organization, OrganizationMember class OrgSerializer(ModelSerializer): diff --git a/apps/tickets/api/request_asset_perm.py b/apps/tickets/api/request_asset_perm.py index d33ea34cf..5bfedabe0 100644 --- a/apps/tickets/api/request_asset_perm.py +++ b/apps/tickets/api/request_asset_perm.py @@ -1,3 +1,5 @@ +import textwrap + from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from rest_framework.decorators import action @@ -62,13 +64,13 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet): ips = ', '.join(meta.get('ips', [])) confirmed_assets = ', '.join(meta.get('confirmed_assets', [])) - return f''' + return textwrap.dedent(f'''\ {_('IP group')}: {ips} {_('Hostname')}: {meta.get('hostname', '')} {_('System user')}: {meta.get('system_user', '')} {_('Confirmed assets')}: {confirmed_assets} {_('Confirmed system user')}: {meta.get('confirmed_system_user', '')} - ''' + ''') @action(detail=True, methods=[POST], permission_classes=[IsAssignee, IsValidUser]) def reject(self, request, *args, **kwargs):