Merge pull request #6224 from jumpserver/feat_account_manager

feat: 添加账号管理相关API
pull/6231/head
Jiangjie.Bai 2021-06-04 11:15:53 +08:00 committed by GitHub
commit f26483c9cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 109 additions and 79 deletions

View File

@ -2,18 +2,49 @@
# #
from orgs.mixins.api import OrgBulkModelViewSet 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 .. 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): class ApplicationViewSet(OrgBulkModelViewSet):
model = models.Application model = Application
filterset_fields = ('name', 'type', 'category') filterset_fields = ('name', 'type', 'category')
search_fields = filterset_fields search_fields = filterset_fields
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.ApplicationSerializer 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

View File

@ -14,6 +14,7 @@ router.register(r'applications', api.ApplicationViewSet, 'application')
urlpatterns = [ urlpatterns = [
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'), path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
path('application-users/', api.ApplicationUserListApi.as_view(), name='application-user')
] ]

View File

@ -31,11 +31,11 @@ class BaseBackend:
def qs_to_values(qs): def qs_to_values(qs):
values = qs.values( values = qs.values(
'hostname', 'ip', "asset_id", 'hostname', 'ip', "asset_id",
'username', 'password', 'private_key', 'public_key', 'name', 'username', 'password', 'private_key', 'public_key',
'score', 'version', 'score', 'version',
"asset_username", "union_id", "asset_username", "union_id",
'date_created', 'date_updated', 'date_created', 'date_updated',
'org_id', 'backend', 'org_id', 'backend', 'backend_display'
) )
return values return values

View File

@ -106,6 +106,7 @@ class DBBackend(BaseBackend):
class SystemUserBackend(DBBackend): class SystemUserBackend(DBBackend):
model = SystemUser.assets.through model = SystemUser.assets.through
backend = 'system_user' backend = 'system_user'
backend_display = _('System user')
prefer = backend prefer = backend
base_score = 0 base_score = 0
union_id_length = 2 union_id_length = 2
@ -138,6 +139,7 @@ class SystemUserBackend(DBBackend):
kwargs = dict( kwargs = dict(
hostname=F("asset__hostname"), hostname=F("asset__hostname"),
ip=F("asset__ip"), ip=F("asset__ip"),
name=F("systemuser__name"),
username=F("systemuser__username"), username=F("systemuser__username"),
password=F("systemuser__password"), password=F("systemuser__password"),
private_key=F("systemuser__private_key"), private_key=F("systemuser__private_key"),
@ -152,7 +154,8 @@ class SystemUserBackend(DBBackend):
union_id=Concat(F("systemuser_id"), Value("_"), F("asset_id"), union_id=Concat(F("systemuser_id"), Value("_"), F("asset_id"),
output_field=CharField()), output_field=CharField()),
org_id=F("asset__org_id"), 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 return kwargs
@ -174,12 +177,17 @@ class SystemUserBackend(DBBackend):
class DynamicSystemUserBackend(SystemUserBackend): class DynamicSystemUserBackend(SystemUserBackend):
backend = 'system_user_dynamic' backend = 'system_user_dynamic'
backend_display = _('System user(Dynamic)')
prefer = 'system_user' prefer = 'system_user'
union_id_length = 3 union_id_length = 3
def get_annotate(self): def get_annotate(self):
kwargs = super().get_annotate() kwargs = super().get_annotate()
kwargs.update(dict( kwargs.update(dict(
name=Concat(
F("systemuser__users__name"), Value('('), F("systemuser__name"), Value(')'),
output_field=CharField()
),
username=F("systemuser__users__username"), username=F("systemuser__users__username"),
asset_username=Concat( asset_username=Concat(
F("asset__id"), Value("_"), F("asset__id"), Value("_"),
@ -221,6 +229,7 @@ class DynamicSystemUserBackend(SystemUserBackend):
class AdminUserBackend(DBBackend): class AdminUserBackend(DBBackend):
model = Asset model = Asset
backend = 'admin_user' backend = 'admin_user'
backend_display = _('Admin user')
prefer = backend prefer = backend
base_score = 200 base_score = 200
@ -246,6 +255,7 @@ class AdminUserBackend(DBBackend):
def all(self): def all(self):
qs = self.model.objects.all().annotate( qs = self.model.objects.all().annotate(
asset_id=F("id"), asset_id=F("id"),
name=F("admin_user__name"),
username=F("admin_user__username"), username=F("admin_user__username"),
password=F("admin_user__password"), password=F("admin_user__password"),
private_key=F("admin_user__private_key"), 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()), 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()), union_id=Concat(F("admin_user_id"), Value("_"), F("id"), output_field=CharField()),
backend=Value(self.backend, CharField()), backend=Value(self.backend, CharField()),
backend_display=Value(self.backend_display, CharField()),
) )
qs = self.qs_to_values(qs) qs = self.qs_to_values(qs)
return qs return qs
@ -264,6 +275,7 @@ class AdminUserBackend(DBBackend):
class AuthbookBackend(DBBackend): class AuthbookBackend(DBBackend):
model = AuthBook model = AuthBook
backend = 'db' backend = 'db'
backend_display = _('Database')
prefer = backend prefer = backend
base_score = 400 base_score = 400
@ -313,6 +325,7 @@ class AuthbookBackend(DBBackend):
asset_username=Concat(F("asset__id"), Value("_"), F("username"), output_field=CharField()), asset_username=Concat(F("asset__id"), Value("_"), F("username"), output_field=CharField()),
union_id=Concat(F("id"), Value("_"), F("asset_id"), output_field=CharField()), union_id=Concat(F("id"), Value("_"), F("asset_id"), output_field=CharField()),
backend=Value(self.backend, CharField()), backend=Value(self.backend, CharField()),
backend_display=Value(self.backend_display, CharField()),
) )
qs = self.qs_to_values(qs) qs = self.qs_to_values(qs)
return qs return qs

View File

@ -7,6 +7,7 @@ class AssetUser(AuthBook):
hostname = "" hostname = ""
ip = "" ip = ""
backend = "" backend = ""
backend_display = ""
union_id = "" union_id = ""
asset_username = "" asset_username = ""

View File

@ -47,16 +47,17 @@ class AssetUserReadSerializer(AssetUserWriteSerializer):
ip = serializers.CharField(read_only=True, label=_("IP")) ip = serializers.CharField(read_only=True, label=_("IP"))
asset = serializers.CharField(source='asset_id', label=_('Asset')) asset = serializers.CharField(source='asset_id', label=_('Asset'))
backend = serializers.CharField(read_only=True, label=_("Backend")) backend = serializers.CharField(read_only=True, label=_("Backend"))
backend_display = serializers.CharField(read_only=True, label=_("Source"))
class Meta(AssetUserWriteSerializer.Meta): class Meta(AssetUserWriteSerializer.Meta):
read_only_fields = ( read_only_fields = (
'date_created', 'date_updated', 'date_created', 'date_updated',
'created_by', 'version', 'created_by', 'version',
) )
fields_mini = ['id', 'username'] fields_mini = ['id', 'name', 'username']
fields_write_only = ['password', 'private_key', "public_key"] fields_write_only = ['password', 'private_key', "public_key"]
fields_small = fields_mini + fields_write_only + [ fields_small = fields_mini + fields_write_only + [
'backend', 'version', 'backend', 'backend_display', 'version',
'date_created', "date_updated", 'date_created', "date_updated",
'comment' 'comment'
] ]

Binary file not shown.

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-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" "PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -121,7 +121,7 @@ msgstr "系统用户"
#: applications/serializers/attrs/application_category/remote_app.py:33 #: applications/serializers/attrs/application_category/remote_app.py:33
#: assets/models/asset.py:355 assets/models/authbook.py:26 #: assets/models/asset.py:355 assets/models/authbook.py:26
#: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:34 #: 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 #: assets/serializers/system_user.py:201 audits/models.py:38
#: perms/models/asset_permission.py:99 templates/index.html:82 #: perms/models/asset_permission.py:99 templates/index.html:82
#: terminal/backends/command/models.py:19 #: terminal/backends/command/models.py:19
@ -184,7 +184,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
#: users/templates/users/_select_user_modal.html:14 #: users/templates/users/_select_user_modal.html:14
#: xpack/plugins/change_auth_plan/models.py:47 #: xpack/plugins/change_auth_plan/models.py:47
#: xpack/plugins/change_auth_plan/models.py:278 #: xpack/plugins/change_auth_plan/models.py:278
#: xpack/plugins/cloud/serializers.py:51 #: xpack/plugins/cloud/serializers.py:65
msgid "Username" msgid "Username"
msgstr "用户名" msgstr "用户名"
@ -233,6 +233,7 @@ msgstr "所有复核人都不属于组织 `{}`"
#: applications/const.py:9 #: applications/const.py:9
#: applications/serializers/attrs/application_category/db.py:14 #: applications/serializers/attrs/application_category/db.py:14
#: applications/serializers/attrs/application_type/mysql_workbench.py:26 #: applications/serializers/attrs/application_type/mysql_workbench.py:26
#: assets/backends/db.py:278
msgid "Database" msgid "Database"
msgstr "数据库" msgstr "数据库"
@ -285,7 +286,7 @@ msgid "Cluster"
msgstr "集群" msgstr "集群"
#: applications/serializers/attrs/application_category/db.py:11 #: 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" msgid "Host"
msgstr "主机" msgstr "主机"
@ -295,7 +296,7 @@ msgstr "主机"
#: applications/serializers/attrs/application_type/oracle.py:11 #: applications/serializers/attrs/application_type/oracle.py:11
#: applications/serializers/attrs/application_type/pgsql.py:11 #: applications/serializers/attrs/application_type/pgsql.py:11
#: assets/models/asset.py:188 assets/models/domain.py:53 #: assets/models/asset.py:188 assets/models/domain.py:53
#: xpack/plugins/cloud/serializers.py:50 #: xpack/plugins/cloud/serializers.py:64
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
@ -315,7 +316,7 @@ msgstr "目标URL"
#: applications/serializers/attrs/application_type/custom.py:25 #: applications/serializers/attrs/application_type/custom.py:25
#: applications/serializers/attrs/application_type/mysql_workbench.py:34 #: applications/serializers/attrs/application_type/mysql_workbench.py:34
#: applications/serializers/attrs/application_type/vmware_client.py:30 #: applications/serializers/attrs/application_type/vmware_client.py:30
#: assets/models/base.py: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 #: audits/signals_handler.py:58 authentication/forms.py:22
#: authentication/templates/authentication/login.html:164 #: authentication/templates/authentication/login.html:164
#: settings/serializers/settings.py:93 users/forms/profile.py:21 #: 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:68
#: xpack/plugins/change_auth_plan/models.py:190 #: xpack/plugins/change_auth_plan/models.py:190
#: xpack/plugins/change_auth_plan/models.py:285 #: xpack/plugins/change_auth_plan/models.py:285
#: xpack/plugins/cloud/serializers.py:53 #: xpack/plugins/cloud/serializers.py:67
msgid "Password" msgid "Password"
msgstr "密码" msgstr "密码"
@ -357,11 +358,35 @@ msgstr "不能删除根节点 ({})"
msgid "Deletion failed and the node contains assets" msgid "Deletion failed and the node contains assets"
msgstr "删除失败,节点包含资产" 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" msgid "Could not remove asset admin user"
msgstr "不能移除资产的管理用户账号" msgstr "不能移除资产的管理用户账号"
#: assets/backends/db.py:305 #: assets/backends/db.py:317
msgid "Latest version could not be delete" msgid "Latest version could not be delete"
msgstr "最新版本的不能被删除" msgstr "最新版本的不能被删除"
@ -405,12 +430,6 @@ msgstr "节点"
msgid "Is active" msgid "Is active"
msgstr "激活" 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 #: assets/models/asset.py:199
msgid "Public IP" msgid "Public IP"
msgstr "公网IP" msgstr "公网IP"
@ -678,7 +697,7 @@ msgstr "ssh私钥"
#: users/templates/users/user_asset_permission.html:41 #: users/templates/users/user_asset_permission.html:41
#: users/templates/users/user_asset_permission.html:73 #: users/templates/users/user_asset_permission.html:73
#: users/templates/users/user_asset_permission.html:158 #: users/templates/users/user_asset_permission.html:158
#: 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" msgid "Node"
msgstr "节点" msgstr "节点"
@ -740,20 +759,6 @@ msgstr "家目录"
msgid "System groups" msgid "System groups"
msgstr "用户组" 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 #: assets/models/utils.py:35
#, python-format #, python-format
msgid "%(value)s is not an even number" msgid "%(value)s is not an even number"
@ -813,12 +818,16 @@ msgstr "ID"
msgid "Backend" msgid "Backend"
msgstr "后端" 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 #: users/models/user.py:580 users/templates/users/user_password_update.html:48
msgid "Public key" msgid "Public key"
msgstr "SSH公钥" 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" msgid "Private key"
msgstr "ssh私钥" msgstr "ssh私钥"
@ -3890,7 +3899,7 @@ msgid "Wechat"
msgstr "微信" msgstr "微信"
#: users/models/user.py:596 #: users/models/user.py:596
msgid "Source" msgid "User source"
msgstr "用户来源" msgstr "用户来源"
#: users/models/user.py:600 #: users/models/user.py:600
@ -4003,7 +4012,7 @@ msgid "Security token validation"
msgstr "安全令牌验证" msgstr "安全令牌验证"
#: users/templates/users/_base_otp.html:14 xpack/plugins/cloud/models.py:78 #: 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" msgid "Account"
msgstr "账户" msgstr "账户"
@ -4744,7 +4753,7 @@ msgstr "云服务商"
msgid "Cloud account" msgid "Cloud account"
msgstr "云账号" 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" msgid "Regions"
msgstr "地域" msgstr "地域"
@ -4752,7 +4761,7 @@ msgstr "地域"
msgid "Hostname strategy" msgid "Hostname strategy"
msgstr "主机名策略" 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" msgid "Always update"
msgstr "总是更新" msgstr "总是更新"
@ -4944,20 +4953,24 @@ msgstr ""
msgid "Subscription ID" msgid "Subscription ID"
msgstr "" 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" msgid "History count"
msgstr "执行次数" msgstr "执行次数"
#: xpack/plugins/cloud/serializers.py:125 #: xpack/plugins/cloud/serializers.py:139
msgid "Instance count" msgid "Instance count"
msgstr "实例个数" msgstr "实例个数"
#: xpack/plugins/cloud/serializers.py:148 #: xpack/plugins/cloud/serializers.py:162
#: xpack/plugins/gathered_user/serializers.py:20 #: xpack/plugins/gathered_user/serializers.py:20
msgid "Periodic display" msgid "Periodic display"
msgstr "定时执行" msgstr "定时执行"
#: xpack/plugins/cloud/utils.py:64 #: xpack/plugins/cloud/utils.py:65
msgid "Account unavailable" msgid "Account unavailable"
msgstr "账户无效" msgstr "账户无效"
@ -5044,33 +5057,3 @@ msgstr "旗舰版"
#: xpack/plugins/license/models.py:77 #: xpack/plugins/license/models.py:77
msgid "Community edition" msgid "Community edition"
msgstr "社区版" 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 "无"

View File

@ -593,7 +593,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
source = models.CharField( source = models.CharField(
max_length=30, default=Source.local, max_length=30, default=Source.local,
choices=Source.choices, choices=Source.choices,
verbose_name=_('Source') verbose_name=_('User source')
) )
date_password_last_updated = models.DateTimeField( date_password_last_updated = models.DateTimeField(
auto_now_add=True, blank=True, null=True, auto_now_add=True, blank=True, null=True,