diff --git a/apps/audits/signals_handler.py b/apps/audits/signals_handler.py index 5f94fb42a..05de237d3 100644 --- a/apps/audits/signals_handler.py +++ b/apps/audits/signals_handler.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -from django.db.models.signals import post_save, post_delete +from django.db.models.signals import post_save, post_delete, m2m_changed from django.dispatch import receiver from django.conf import settings from django.db import transaction @@ -11,6 +11,8 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework.renderers import JSONRenderer from rest_framework.request import Request +from assets.models import Asset +from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR from jumpserver.utils import current_request from common.utils import get_request_ip, get_logger, get_syslogger from users.models import User @@ -20,6 +22,9 @@ from terminal.models import Session, Command from common.utils.encode import model_to_json from .utils import write_login_log from . import models +from .models import OperateLog +from orgs.utils import current_org +from perms.models import AssetPermission, ApplicationPermission logger = get_logger(__name__) sys_logger = get_syslogger(__name__) @@ -90,6 +95,119 @@ def create_operate_log(action, sender, resource): logger.error("Create operate log error: {}".format(e)) +M2M_NEED_RECORD = { + 'OrganizationMember': ( + _('User and Organization'), + _('{User} *JOINED* {Organization}'), + _('{User} *LEFT* {Organization}') + ), + User.groups.through._meta.object_name: ( + _('User and Group'), + _('{User} *JOINED* {UserGroup}'), + _('{User} *LEFT* {UserGroup}') + ), + Asset.nodes.through._meta.object_name: ( + _('Node and Asset'), + _('{Node} *ADD* {Asset}'), + _('{Node} *REMOVE* {Asset}') + ), + AssetPermission.users.through._meta.object_name: ( + _('User asset permissions'), + _('{AssetPermission} *ADD* {User}'), + _('{AssetPermission} *REMOVE* {User}'), + ), + AssetPermission.user_groups.through._meta.object_name: ( + _('User group asset permissions'), + _('{AssetPermission} *ADD* {UserGroup}'), + _('{AssetPermission} *REMOVE* {UserGroup}'), + ), + AssetPermission.assets.through._meta.object_name: ( + _('Asset permission'), + _('{AssetPermission} *ADD* {Asset}'), + _('{AssetPermission} *REMOVE* {Asset}'), + ), + AssetPermission.nodes.through._meta.object_name: ( + _('Node permission'), + _('{AssetPermission} *ADD* {Node}'), + _('{AssetPermission} *REMOVE* {Node}'), + ), + AssetPermission.system_users.through._meta.object_name: ( + _('Asset permission and SystemUser'), + _('{AssetPermission} *ADD* {SystemUser}'), + _('{AssetPermission} *REMOVE* {SystemUser}'), + ), + ApplicationPermission.users.through._meta.object_name: ( + _('User application permissions'), + _('{ApplicationPermission} *ADD* {User}'), + _('{ApplicationPermission} *REMOVE* {User}'), + ), + ApplicationPermission.user_groups.through._meta.object_name: ( + _('User group application permissions'), + _('{ApplicationPermission} *ADD* {UserGroup}'), + _('{ApplicationPermission} *REMOVE* {UserGroup}'), + ), + ApplicationPermission.applications.through._meta.object_name: ( + _('Application permission'), + _('{ApplicationPermission} *ADD* {Application}'), + _('{ApplicationPermission} *REMOVE* {Application}'), + ), + ApplicationPermission.system_users.through._meta.object_name: ( + _('Application permission and SystemUser'), + _('{ApplicationPermission} *ADD* {SystemUser}'), + _('{ApplicationPermission} *REMOVE* {SystemUser}'), + ), +} + + +M2M_ACTION = { + POST_ADD: 'add', + POST_REMOVE: 'remove', + POST_CLEAR: 'remove', +} + + +@receiver(m2m_changed) +def on_m2m_changed(sender, action, instance, reverse, model, pk_set, **kwargs): + if action not in M2M_ACTION: + return + + user = current_request.user if current_request else None + if not user or not user.is_authenticated: + return + + sender_name = sender._meta.object_name + if sender_name in M2M_NEED_RECORD: + action = M2M_ACTION[action] + org_id = current_org.id + remote_addr = get_request_ip(current_request) + user = str(user) + resource_type, resource_tmpl_add, resource_tmpl_remove = M2M_NEED_RECORD[sender_name] + if action == 'add': + resource_tmpl = resource_tmpl_add + elif action == 'remove': + resource_tmpl = resource_tmpl_remove + + to_create = [] + objs = model.objects.filter(pk__in=pk_set) + + instance_name = instance._meta.object_name + instance_value = str(instance) + + model_name = model._meta.object_name + + for obj in objs: + resource = resource_tmpl.format(**{ + instance_name: instance_value, + model_name: str(obj) + }) + + to_create.append(OperateLog( + user=user, action=action, resource_type=resource_type, + resource=resource, remote_addr=remote_addr, org_id=org_id + )) + OperateLog.objects.bulk_create(to_create) + + @receiver(post_save) def on_object_created_or_update(sender, instance=None, created=False, update_fields=None, **kwargs): # last_login 改变是最后登录日期, 每次登录都会改变 diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 07fed0963..3239dd59c 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 013697fec..c1b36c65f 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-07-26 15:48+0800\n" +"POT-Creation-Date: 2021-07-26 17:56+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -140,7 +140,7 @@ msgstr "审批人" msgid "Login asset confirm" msgstr "登录资产复核" -#: acls/serializers/login_acl.py:18 xpack/plugins/cloud/serializers.py:164 +#: acls/serializers/login_acl.py:18 xpack/plugins/cloud/serializers.py:165 msgid "IP address invalid: `{}`" msgstr "IP 地址无效: `{}`" @@ -330,7 +330,7 @@ msgstr "目标URL" #: applications/serializers/attrs/application_type/custom.py:25 #: applications/serializers/attrs/application_type/mysql_workbench.py:34 #: applications/serializers/attrs/application_type/vmware_client.py:30 -#: assets/models/base.py:177 audits/signals_handler.py:58 +#: assets/models/base.py:177 audits/signals_handler.py:63 #: authentication/forms.py:22 #: authentication/templates/authentication/login.html:164 #: settings/serializers/settings.py:94 users/forms/profile.py:21 @@ -392,7 +392,7 @@ msgstr "系统平台" #: assets/models/asset.py:186 assets/serializers/asset.py:65 #: perms/serializers/asset/user_permission.py:41 -#: xpack/plugins/cloud/models.py:99 xpack/plugins/cloud/serializers.py:182 +#: xpack/plugins/cloud/models.py:99 xpack/plugins/cloud/serializers.py:183 msgid "Protocols" msgstr "协议组" @@ -411,7 +411,7 @@ msgstr "激活" #: assets/models/asset.py:193 assets/models/cluster.py:19 #: assets/models/user.py:191 assets/models/user.py:326 templates/_nav.html:44 -#: xpack/plugins/cloud/models.py:96 xpack/plugins/cloud/serializers.py:204 +#: xpack/plugins/cloud/models.py:96 xpack/plugins/cloud/serializers.py:205 msgid "Admin user" msgstr "特权用户" @@ -713,7 +713,7 @@ msgstr "ssh私钥" #: users/templates/users/user_asset_permission.html:41 #: users/templates/users/user_asset_permission.html:73 #: users/templates/users/user_asset_permission.html:158 -#: xpack/plugins/cloud/models.py:93 xpack/plugins/cloud/serializers.py:205 +#: xpack/plugins/cloud/models.py:93 xpack/plugins/cloud/serializers.py:206 msgid "Node" msgstr "节点" @@ -1198,30 +1198,190 @@ msgstr "运行用户" msgid "User display" msgstr "用户" -#: audits/signals_handler.py:57 +#: audits/signals_handler.py:62 msgid "SSH Key" msgstr "SSH 密钥" -#: audits/signals_handler.py:59 +#: audits/signals_handler.py:64 msgid "SSO" msgstr "" -#: audits/signals_handler.py:60 +#: audits/signals_handler.py:65 msgid "Auth Token" msgstr "认证令牌" -#: audits/signals_handler.py:61 +#: audits/signals_handler.py:66 #: authentication/templates/authentication/login.html:210 #: notifications/backends/__init__.py:12 msgid "WeCom" msgstr "企业微信" -#: audits/signals_handler.py:62 +#: audits/signals_handler.py:67 #: authentication/templates/authentication/login.html:215 #: notifications/backends/__init__.py:13 msgid "DingTalk" msgstr "钉钉" +#: audits/signals_handler.py:100 +msgid "User and Organization" +msgstr "用户与组织" + +#: audits/signals_handler.py:101 +#, python-brace-format +msgid "{User} *JOINED* {Organization}" +msgstr "{User} *加入了* {Organization}" + +#: audits/signals_handler.py:102 +msgid "{User} *LEFT* {Organization}" +msgstr "{User} *离开了* {Organization}" + +#: audits/signals_handler.py:105 +msgid "User and Group" +msgstr "用户与用户组" + +#: audits/signals_handler.py:106 +#, python-brace-format +msgid "{User} *JOINED* {UserGroup}" +msgstr "{User} *加入了* {UserGroup}" + +#: audits/signals_handler.py:107 +#, python-brace-format +msgid "{User} *LEFT* {UserGroup}" +msgstr "{User} *离开了* {UserGroup}" + +#: audits/signals_handler.py:110 +msgid "Node and Asset" +msgstr "节点与资产" + +#: audits/signals_handler.py:111 +#, python-brace-format +msgid "{Node} *ADD* {Asset}" +msgstr "{Node} *添加了* {Asset}" + +#: audits/signals_handler.py:112 +#, python-brace-format +msgid "{Node} *REMOVE* {Asset}" +msgstr "{Node} *移除了* {Asset}" + +#: audits/signals_handler.py:115 +msgid "User asset permissions" +msgstr "用户资产授权" + +#: audits/signals_handler.py:116 +msgid "{AssetPermission} *ADD* {User}" +msgstr "{AssetPermission} *添加了* {User}" + +#: audits/signals_handler.py:117 +#, python-brace-format +msgid "{AssetPermission} *REMOVE* {User}" +msgstr "{AssetPermission} *移除了* {User}" + +#: audits/signals_handler.py:120 +msgid "User group asset permissions" +msgstr "用户组资产授权" + +#: audits/signals_handler.py:121 +#, python-brace-format +msgid "{AssetPermission} *ADD* {UserGroup}" +msgstr "{AssetPermission} *添加了* {UserGroup}" + +#: audits/signals_handler.py:122 +#, python-brace-format +msgid "{AssetPermission} *REMOVE* {UserGroup}" +msgstr "{AssetPermission} *移除了* {UserGroup}" + +#: audits/signals_handler.py:125 perms/models/asset_permission.py:106 +#: templates/_nav.html:78 users/templates/users/_user_detail_nav_header.html:31 +msgid "Asset permission" +msgstr "资产授权" + +#: audits/signals_handler.py:126 +#, python-brace-format +msgid "{AssetPermission} *ADD* {Asset}" +msgstr "{AssetPermission} *添加了* {Asset}" + +#: audits/signals_handler.py:127 +#, python-brace-format +msgid "{AssetPermission} *REMOVE* {Asset}" +msgstr "{AssetPermission} *移除了* {Asset}" + +#: audits/signals_handler.py:130 +msgid "Node permission" +msgstr "节点授权" + +#: audits/signals_handler.py:131 +msgid "{AssetPermission} *ADD* {Node}" +msgstr "{AssetPermission} *添加了* {Node}" + +#: audits/signals_handler.py:132 +#, python-brace-format +msgid "{AssetPermission} *REMOVE* {Node}" +msgstr "{AssetPermission} *移除了* {Node}" + +#: audits/signals_handler.py:135 +msgid "Asset permission and SystemUser" +msgstr "资产授权与系统用户" + +#: audits/signals_handler.py:136 +#, python-brace-format +msgid "{AssetPermission} *ADD* {SystemUser}" +msgstr "{AssetPermission} *添加了* {SystemUser}" + +#: audits/signals_handler.py:137 +#, python-brace-format +msgid "{AssetPermission} *REMOVE* {SystemUser}" +msgstr "{AssetPermission} *移除了* {SystemUser}" + +#: audits/signals_handler.py:140 +msgid "User application permissions" +msgstr "用户应用授权" + +#: audits/signals_handler.py:141 +msgid "{ApplicationPermission} *ADD* {User}" +msgstr "{ApplicationPermission} *添加了* {User}" + +#: audits/signals_handler.py:142 +msgid "{ApplicationPermission} *REMOVE* {User}" +msgstr "{ApplicationPermission} *移除了* {User}" + +#: audits/signals_handler.py:145 +msgid "User group application permissions" +msgstr "用户组应用授权" + +#: audits/signals_handler.py:146 +msgid "{ApplicationPermission} *ADD* {UserGroup}" +msgstr "{ApplicationPermission} *添加了* {UserGroup}" + +#: audits/signals_handler.py:147 +msgid "{ApplicationPermission} *REMOVE* {UserGroup}" +msgstr "{ApplicationPermission} *移除了* {UserGroup}" + +#: audits/signals_handler.py:150 perms/models/application_permission.py:36 +msgid "Application permission" +msgstr "应用管理" + +#: audits/signals_handler.py:151 +msgid "{ApplicationPermission} *ADD* {Application}" +msgstr "{ApplicationPermission} *添加了* {Application}" + +#: audits/signals_handler.py:152 +#, python-brace-format +msgid "{ApplicationPermission} *REMOVE* {Application}" +msgstr "{ApplicationPermission} *移除了* {Application}" + +#: audits/signals_handler.py:155 +msgid "Application permission and SystemUser" +msgstr "应用授权与系统用户" + +#: audits/signals_handler.py:156 +msgid "{ApplicationPermission} *ADD* {SystemUser}" +msgstr "{ApplicationPermission} *添加了* {SystemUser}" + +#: audits/signals_handler.py:157 +#, python-brace-format +msgid "{ApplicationPermission} *REMOVE* {SystemUser}" +msgstr "{ApplicationPermission} *移除了* {SystemUser}" + #: authentication/api/connection_token.py:258 msgid "Invalid token" msgstr "无效的令牌" @@ -2043,10 +2203,6 @@ msgstr "该授权暂时不能撤销" msgid "Application" msgstr "应用程序" -#: perms/models/application_permission.py:36 -msgid "Application permission" -msgstr "应用管理" - #: perms/models/asset_permission.py:37 settings/serializers/settings.py:117 msgid "All" msgstr "全部" @@ -2084,11 +2240,6 @@ msgstr "剪贴板复制粘贴" msgid "Actions" msgstr "动作" -#: perms/models/asset_permission.py:106 templates/_nav.html:78 -#: users/templates/users/_user_detail_nav_header.html:31 -msgid "Asset permission" -msgstr "资产授权" - #: perms/models/asset_permission.py:209 msgid "Ungrouped" msgstr "未分组" @@ -4794,11 +4945,11 @@ msgstr "云服务商" msgid "Cloud account" msgstr "云账号" -#: xpack/plugins/cloud/models.py:82 xpack/plugins/cloud/serializers.py:203 +#: xpack/plugins/cloud/models.py:82 xpack/plugins/cloud/serializers.py:204 msgid "Account" msgstr "账户" -#: xpack/plugins/cloud/models.py:85 xpack/plugins/cloud/serializers.py:178 +#: xpack/plugins/cloud/models.py:85 xpack/plugins/cloud/serializers.py:179 msgid "Regions" msgstr "地域" @@ -4806,11 +4957,11 @@ msgstr "地域" msgid "Hostname strategy" msgstr "主机名策略" -#: xpack/plugins/cloud/models.py:102 xpack/plugins/cloud/serializers.py:185 +#: xpack/plugins/cloud/models.py:102 xpack/plugins/cloud/serializers.py:186 msgid "IP network segment group" msgstr "IP网段组" -#: xpack/plugins/cloud/models.py:105 xpack/plugins/cloud/serializers.py:207 +#: xpack/plugins/cloud/models.py:105 xpack/plugins/cloud/serializers.py:208 msgid "Always update" msgstr "总是更新" @@ -5010,7 +5161,7 @@ msgstr "这个字段是必填项" msgid "API Endpoint" msgstr "API 端点" -#: xpack/plugins/cloud/serializers.py:170 +#: xpack/plugins/cloud/serializers.py:171 msgid "" "The IP address that is first matched to will be used as the IP of the " "created asset.
The default * indicates a random match.
Format for " @@ -5019,15 +5170,15 @@ msgstr "" "第一个匹配到的 IP 地址将被用作创建的资产的 IP。
默认值 * 表示随机匹配。" "
格式为以逗号分隔的字符串,例如:192.168.1.0/24,10.1.1.1-10.1.1.20" -#: xpack/plugins/cloud/serializers.py:176 +#: xpack/plugins/cloud/serializers.py:177 msgid "History count" msgstr "执行次数" -#: xpack/plugins/cloud/serializers.py:177 +#: xpack/plugins/cloud/serializers.py:178 msgid "Instance count" msgstr "实例个数" -#: xpack/plugins/cloud/serializers.py:206 +#: xpack/plugins/cloud/serializers.py:207 #: xpack/plugins/gathered_user/serializers.py:20 msgid "Periodic display" msgstr "定时执行"