diff --git a/apps/applications/api/application.py b/apps/applications/api/application.py index cb51ff023..84426b6b2 100644 --- a/apps/applications/api/application.py +++ b/apps/applications/api/application.py @@ -2,18 +2,49 @@ # from orgs.mixins.api import OrgBulkModelViewSet +from rest_framework import generics -from ..hands import IsOrgAdminOrAppUser +from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin from .. import models, serializers +from ..models import Application +from assets.models import SystemUser +from assets.serializers import SystemUserListSerializer +from perms.models import ApplicationPermission +from ..const import ApplicationCategoryChoices -__all__ = ['ApplicationViewSet'] +__all__ = ['ApplicationViewSet', 'ApplicationUserListApi'] class ApplicationViewSet(OrgBulkModelViewSet): - model = models.Application + model = Application filterset_fields = ('name', 'type', 'category') search_fields = filterset_fields permission_classes = (IsOrgAdminOrAppUser,) serializer_class = serializers.ApplicationSerializer + +class ApplicationUserListApi(generics.ListAPIView): + permission_classes = (IsOrgAdmin, ) + filterset_fields = ('name', 'username') + search_fields = filterset_fields + serializer_class = SystemUserListSerializer + + def get_application(self): + application = None + app_id = self.request.query_params.get('application_id') + if app_id: + application = Application.objects.get(id=app_id) + return application + + def get_queryset(self): + queryset = SystemUser.objects.none() + application = self.get_application() + if not application: + return queryset + system_user_ids = ApplicationPermission.objects.filter(applications=application)\ + .values_list('system_users', flat=True) + if not system_user_ids: + return queryset + queryset = SystemUser.objects.filter(id__in=system_user_ids) + return queryset diff --git a/apps/applications/urls/api_urls.py b/apps/applications/urls/api_urls.py index ab463a401..9ca50d32c 100644 --- a/apps/applications/urls/api_urls.py +++ b/apps/applications/urls/api_urls.py @@ -14,6 +14,7 @@ router.register(r'applications', api.ApplicationViewSet, 'application') urlpatterns = [ path('remote-apps//connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'), + path('application-users/', api.ApplicationUserListApi.as_view(), name='application-user') ] diff --git a/apps/assets/backends/base.py b/apps/assets/backends/base.py index 3b27a57af..17115afaa 100644 --- a/apps/assets/backends/base.py +++ b/apps/assets/backends/base.py @@ -31,11 +31,11 @@ class BaseBackend: def qs_to_values(qs): values = qs.values( 'hostname', 'ip', "asset_id", - 'username', 'password', 'private_key', 'public_key', + 'name', 'username', 'password', 'private_key', 'public_key', 'score', 'version', "asset_username", "union_id", 'date_created', 'date_updated', - 'org_id', 'backend', + 'org_id', 'backend', 'backend_display' ) return values diff --git a/apps/assets/backends/db.py b/apps/assets/backends/db.py index 386f0ee29..aa3e1ef78 100644 --- a/apps/assets/backends/db.py +++ b/apps/assets/backends/db.py @@ -106,6 +106,7 @@ class DBBackend(BaseBackend): class SystemUserBackend(DBBackend): model = SystemUser.assets.through backend = 'system_user' + backend_display = _('System user') prefer = backend base_score = 0 union_id_length = 2 @@ -138,6 +139,7 @@ class SystemUserBackend(DBBackend): kwargs = dict( hostname=F("asset__hostname"), ip=F("asset__ip"), + name=F("systemuser__name"), username=F("systemuser__username"), password=F("systemuser__password"), private_key=F("systemuser__private_key"), @@ -152,7 +154,8 @@ class SystemUserBackend(DBBackend): union_id=Concat(F("systemuser_id"), Value("_"), F("asset_id"), output_field=CharField()), org_id=F("asset__org_id"), - backend=Value(self.backend, CharField()) + backend=Value(self.backend, CharField()), + backend_display=Value(self.backend_display, CharField()), ) return kwargs @@ -174,12 +177,17 @@ class SystemUserBackend(DBBackend): class DynamicSystemUserBackend(SystemUserBackend): backend = 'system_user_dynamic' + backend_display = _('System user(Dynamic)') prefer = 'system_user' union_id_length = 3 def get_annotate(self): kwargs = super().get_annotate() kwargs.update(dict( + name=Concat( + F("systemuser__users__name"), Value('('), F("systemuser__name"), Value(')'), + output_field=CharField() + ), username=F("systemuser__users__username"), asset_username=Concat( F("asset__id"), Value("_"), @@ -221,6 +229,7 @@ class DynamicSystemUserBackend(SystemUserBackend): class AdminUserBackend(DBBackend): model = Asset backend = 'admin_user' + backend_display = _('Admin user') prefer = backend base_score = 200 @@ -246,6 +255,7 @@ class AdminUserBackend(DBBackend): def all(self): qs = self.model.objects.all().annotate( asset_id=F("id"), + name=F("admin_user__name"), username=F("admin_user__username"), password=F("admin_user__password"), private_key=F("admin_user__private_key"), @@ -256,6 +266,7 @@ class AdminUserBackend(DBBackend): asset_username=Concat(F("id"), Value("_"), F("admin_user__username"), output_field=CharField()), union_id=Concat(F("admin_user_id"), Value("_"), F("id"), output_field=CharField()), backend=Value(self.backend, CharField()), + backend_display=Value(self.backend_display, CharField()), ) qs = self.qs_to_values(qs) return qs @@ -264,6 +275,7 @@ class AdminUserBackend(DBBackend): class AuthbookBackend(DBBackend): model = AuthBook backend = 'db' + backend_display = _('Database') prefer = backend base_score = 400 @@ -313,6 +325,7 @@ class AuthbookBackend(DBBackend): asset_username=Concat(F("asset__id"), Value("_"), F("username"), output_field=CharField()), union_id=Concat(F("id"), Value("_"), F("asset_id"), output_field=CharField()), backend=Value(self.backend, CharField()), + backend_display=Value(self.backend_display, CharField()), ) qs = self.qs_to_values(qs) return qs diff --git a/apps/assets/models/asset_user.py b/apps/assets/models/asset_user.py index 118d4549b..ac9112427 100644 --- a/apps/assets/models/asset_user.py +++ b/apps/assets/models/asset_user.py @@ -7,6 +7,7 @@ class AssetUser(AuthBook): hostname = "" ip = "" backend = "" + backend_display = "" union_id = "" asset_username = "" diff --git a/apps/assets/serializers/asset_user.py b/apps/assets/serializers/asset_user.py index 19cb2adc7..6552281c6 100644 --- a/apps/assets/serializers/asset_user.py +++ b/apps/assets/serializers/asset_user.py @@ -47,16 +47,17 @@ class AssetUserReadSerializer(AssetUserWriteSerializer): ip = serializers.CharField(read_only=True, label=_("IP")) asset = serializers.CharField(source='asset_id', label=_('Asset')) backend = serializers.CharField(read_only=True, label=_("Backend")) + backend_display = serializers.CharField(read_only=True, label=_("Source")) class Meta(AssetUserWriteSerializer.Meta): read_only_fields = ( 'date_created', 'date_updated', 'created_by', 'version', ) - fields_mini = ['id', 'username'] + fields_mini = ['id', 'name', 'username'] fields_write_only = ['password', 'private_key', "public_key"] fields_small = fields_mini + fields_write_only + [ - 'backend', 'version', + 'backend', 'backend_display', 'version', 'date_created', "date_updated", 'comment' ] diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 53adc1239..d5940b932 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 8f5f38858..af2cbcc87 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-05-22 16:56+0800\n" +"POT-Creation-Date: 2021-06-04 11:11+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -121,7 +121,7 @@ msgstr "系统用户" #: applications/serializers/attrs/application_category/remote_app.py:33 #: assets/models/asset.py:355 assets/models/authbook.py:26 #: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:34 -#: assets/serializers/asset_user.py:48 assets/serializers/asset_user.py:89 +#: assets/serializers/asset_user.py:48 assets/serializers/asset_user.py:90 #: assets/serializers/system_user.py:201 audits/models.py:38 #: perms/models/asset_permission.py:99 templates/index.html:82 #: terminal/backends/command/models.py:19 @@ -184,7 +184,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. " #: users/templates/users/_select_user_modal.html:14 #: xpack/plugins/change_auth_plan/models.py:47 #: xpack/plugins/change_auth_plan/models.py:278 -#: xpack/plugins/cloud/serializers.py:51 +#: xpack/plugins/cloud/serializers.py:65 msgid "Username" msgstr "用户名" @@ -233,6 +233,7 @@ msgstr "所有复核人都不属于组织 `{}`" #: applications/const.py:9 #: applications/serializers/attrs/application_category/db.py:14 #: applications/serializers/attrs/application_type/mysql_workbench.py:26 +#: assets/backends/db.py:278 msgid "Database" msgstr "数据库" @@ -285,7 +286,7 @@ msgid "Cluster" msgstr "集群" #: applications/serializers/attrs/application_category/db.py:11 -#: ops/models/adhoc.py:146 xpack/plugins/cloud/serializers.py:49 +#: ops/models/adhoc.py:146 xpack/plugins/cloud/serializers.py:63 msgid "Host" msgstr "主机" @@ -295,7 +296,7 @@ msgstr "主机" #: applications/serializers/attrs/application_type/oracle.py:11 #: applications/serializers/attrs/application_type/pgsql.py:11 #: assets/models/asset.py:188 assets/models/domain.py:53 -#: xpack/plugins/cloud/serializers.py:50 +#: xpack/plugins/cloud/serializers.py:64 msgid "Port" msgstr "端口" @@ -315,7 +316,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:252 assets/serializers/asset_user.py:76 +#: assets/models/base.py:252 assets/serializers/asset_user.py:77 #: audits/signals_handler.py:58 authentication/forms.py:22 #: authentication/templates/authentication/login.html:164 #: settings/serializers/settings.py:93 users/forms/profile.py:21 @@ -325,7 +326,7 @@ msgstr "目标URL" #: xpack/plugins/change_auth_plan/models.py:68 #: xpack/plugins/change_auth_plan/models.py:190 #: xpack/plugins/change_auth_plan/models.py:285 -#: xpack/plugins/cloud/serializers.py:53 +#: xpack/plugins/cloud/serializers.py:67 msgid "Password" msgstr "密码" @@ -357,11 +358,35 @@ msgstr "不能删除根节点 ({})" msgid "Deletion failed and the node contains assets" msgstr "删除失败,节点包含资产" -#: assets/backends/db.py:244 +#: assets/backends/db.py:109 assets/models/user.py:228 audits/models.py:39 +#: perms/models/application_permission.py:31 +#: perms/models/asset_permission.py:101 templates/_nav.html:45 +#: terminal/backends/command/models.py:20 +#: terminal/backends/command/serializers.py:14 terminal/models/session.py:42 +#: users/templates/users/_granted_assets.html:27 +#: users/templates/users/user_asset_permission.html:42 +#: users/templates/users/user_asset_permission.html:76 +#: users/templates/users/user_asset_permission.html:159 +#: users/templates/users/user_database_app_permission.html:40 +#: users/templates/users/user_database_app_permission.html:67 +msgid "System user" +msgstr "系统用户" + +#: assets/backends/db.py:180 +msgid "System user(Dynamic)" +msgstr "系统用户(动态)" + +#: assets/backends/db.py:232 assets/models/asset.py:196 +#: assets/models/cluster.py:19 assets/models/user.py:66 templates/_nav.html:44 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers.py:160 +msgid "Admin user" +msgstr "管理用户" + +#: assets/backends/db.py:253 msgid "Could not remove asset admin user" msgstr "不能移除资产的管理用户账号" -#: assets/backends/db.py:305 +#: assets/backends/db.py:317 msgid "Latest version could not be delete" msgstr "最新版本的不能被删除" @@ -405,12 +430,6 @@ msgstr "节点" msgid "Is active" msgstr "激活" -#: assets/models/asset.py:196 assets/models/cluster.py:19 -#: assets/models/user.py:66 templates/_nav.html:44 -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers.py:146 -msgid "Admin user" -msgstr "管理用户" - #: assets/models/asset.py:199 msgid "Public IP" msgstr "公网IP" @@ -678,7 +697,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:89 xpack/plugins/cloud/serializers.py:147 +#: xpack/plugins/cloud/models.py:89 xpack/plugins/cloud/serializers.py:161 msgid "Node" msgstr "节点" @@ -740,20 +759,6 @@ msgstr "家目录" msgid "System groups" msgstr "用户组" -#: assets/models/user.py:228 audits/models.py:39 -#: perms/models/application_permission.py:31 -#: perms/models/asset_permission.py:101 templates/_nav.html:45 -#: terminal/backends/command/models.py:20 -#: terminal/backends/command/serializers.py:14 terminal/models/session.py:42 -#: users/templates/users/_granted_assets.html:27 -#: users/templates/users/user_asset_permission.html:42 -#: users/templates/users/user_asset_permission.html:76 -#: users/templates/users/user_asset_permission.html:159 -#: users/templates/users/user_database_app_permission.html:40 -#: users/templates/users/user_database_app_permission.html:67 -msgid "System user" -msgstr "系统用户" - #: assets/models/utils.py:35 #, python-format msgid "%(value)s is not an even number" @@ -813,12 +818,16 @@ msgstr "ID" msgid "Backend" msgstr "后端" -#: assets/serializers/asset_user.py:80 users/forms/profile.py:160 +#: assets/serializers/asset_user.py:50 +msgid "Source" +msgstr "来源" + +#: assets/serializers/asset_user.py:81 users/forms/profile.py:160 #: users/models/user.py:580 users/templates/users/user_password_update.html:48 msgid "Public key" msgstr "SSH公钥" -#: assets/serializers/asset_user.py:84 users/models/user.py:577 +#: assets/serializers/asset_user.py:85 users/models/user.py:577 msgid "Private key" msgstr "ssh私钥" @@ -3890,7 +3899,7 @@ msgid "Wechat" msgstr "微信" #: users/models/user.py:596 -msgid "Source" +msgid "User source" msgstr "用户来源" #: users/models/user.py:600 @@ -4003,7 +4012,7 @@ msgid "Security token validation" msgstr "安全令牌验证" #: users/templates/users/_base_otp.html:14 xpack/plugins/cloud/models.py:78 -#: xpack/plugins/cloud/serializers.py:145 +#: xpack/plugins/cloud/serializers.py:159 msgid "Account" msgstr "账户" @@ -4744,7 +4753,7 @@ msgstr "云服务商" msgid "Cloud account" msgstr "云账号" -#: xpack/plugins/cloud/models.py:81 xpack/plugins/cloud/serializers.py:126 +#: xpack/plugins/cloud/models.py:81 xpack/plugins/cloud/serializers.py:140 msgid "Regions" msgstr "地域" @@ -4752,7 +4761,7 @@ msgstr "地域" msgid "Hostname strategy" msgstr "主机名策略" -#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers.py:149 +#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers.py:163 msgid "Always update" msgstr "总是更新" @@ -4944,20 +4953,24 @@ msgstr "" msgid "Subscription ID" msgstr "" -#: xpack/plugins/cloud/serializers.py:124 +#: xpack/plugins/cloud/serializers.py:49 +msgid "This field is required" +msgstr "这个字段是必填项" + +#: xpack/plugins/cloud/serializers.py:138 msgid "History count" msgstr "执行次数" -#: xpack/plugins/cloud/serializers.py:125 +#: xpack/plugins/cloud/serializers.py:139 msgid "Instance count" msgstr "实例个数" -#: xpack/plugins/cloud/serializers.py:148 +#: xpack/plugins/cloud/serializers.py:162 #: xpack/plugins/gathered_user/serializers.py:20 msgid "Periodic display" msgstr "定时执行" -#: xpack/plugins/cloud/utils.py:64 +#: xpack/plugins/cloud/utils.py:65 msgid "Account unavailable" msgstr "账户无效" @@ -5044,33 +5057,3 @@ msgstr "旗舰版" #: xpack/plugins/license/models.py:77 msgid "Community edition" msgstr "社区版" - -#~ msgid "This field is required" -#~ msgstr "这个字段是必填项" - -#~ msgid "{} is required" -#~ msgstr "{} 字段是必填项" - -#~ msgid "AppSecret is required" -#~ msgstr "AppSecret 是必须的" - -#~ msgid "Corporation ID(corpid)" -#~ msgstr "企业 ID(CorpId)" - -#~ msgid "Agent ID(agentid)" -#~ msgstr "应用 ID(AgentId)" - -#~ msgid "Secret(secret)" -#~ msgstr "秘钥(secret)" - -#~ msgid "AgentId" -#~ msgstr "应用 ID(AgentId)" - -#~ msgid "AppKey" -#~ msgstr "应用 Key(AppKey)" - -#~ msgid "AppSecret" -#~ msgstr "应用密文(AppSecret)" - -#~ msgid "No" -#~ msgstr "无" diff --git a/apps/users/models/user.py b/apps/users/models/user.py index f362e60ac..40bb165e9 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -593,7 +593,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser): source = models.CharField( max_length=30, default=Source.local, choices=Source.choices, - verbose_name=_('Source') + verbose_name=_('User source') ) date_password_last_updated = models.DateTimeField( auto_now_add=True, blank=True, null=True,