Dev remoteapp (#3205)

* [Update] 修改RemoteApp关联的系统用户:从RemoteApp中转移到RemoteAppPermission中(未提交迁移文件)

* [Update] 修改RemoteApp关联的系统用户:提交迁移文件

* [Update] 修改RemoteApp关联的系统用户:修改迁移文件

* [Update] 修改迁移文件1

* [Update] 修改迁移文件2

* [Update] 修改迁移文件3

* [Update] 修改RemoteAppPermsUtil获取系统用户的逻辑
This commit is contained in:
BaiJiangJie 2019-09-12 18:25:22 +08:00 committed by 老广
parent 3a8fad7c7d
commit bdcf9ba153
22 changed files with 250 additions and 88 deletions

View File

@ -89,23 +89,16 @@ class RemoteAppCreateUpdateForm(RemoteAppTypeForms, OrgModelForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
field_asset = self.fields['asset'] field_asset = self.fields['asset']
field_asset.queryset = field_asset.queryset.has_protocol('rdp') field_asset.queryset = field_asset.queryset.has_protocol('rdp')
field_system_user = self.fields['system_user']
field_system_user.queryset = field_system_user.queryset.filter(
protocol=SystemUser.PROTOCOL_RDP
)
class Meta: class Meta:
model = RemoteApp model = RemoteApp
fields = [ fields = [
'name', 'asset', 'system_user', 'type', 'path', 'comment' 'name', 'asset', 'type', 'path', 'comment'
] ]
widgets = { widgets = {
'asset': forms.Select(attrs={ 'asset': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Asset') 'class': 'select2', 'data-placeholder': _('Asset')
}), }),
'system_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('System user')
})
} }
def _clean_params(self): def _clean_params(self):

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-09-09 09:57
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('applications', '0001_initial'),
('perms', '0008_remoteapppermission_system_users'),
]
operations = [
migrations.RemoveField(
model_name='remoteapp',
name='system_user',
),
]

View File

@ -22,10 +22,6 @@ class RemoteApp(OrgModelMixin):
asset = models.ForeignKey( asset = models.ForeignKey(
'assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset') 'assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset')
) )
system_user = models.ForeignKey(
'assets.SystemUser', on_delete=models.CASCADE,
verbose_name=_('System user')
)
type = models.CharField( type = models.CharField(
default=const.REMOTE_APP_TYPE_CHROME, default=const.REMOTE_APP_TYPE_CHROME,
choices=const.REMOTE_APP_TYPE_CHOICES, choices=const.REMOTE_APP_TYPE_CHOICES,
@ -80,10 +76,3 @@ class RemoteApp(OrgModelMixin):
'id': self.asset.id, 'id': self.asset.id,
'hostname': self.asset.hostname 'hostname': self.asset.hostname
} }
@property
def system_user_info(self):
return {
'id': self.system_user.id,
'name': self.system_user.name
}

View File

@ -73,13 +73,13 @@ class RemoteAppSerializer(BulkOrgResourceModelSerializer):
model = RemoteApp model = RemoteApp
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = [ fields = [
'id', 'name', 'asset', 'system_user', 'type', 'path', 'params', 'id', 'name', 'asset', 'type', 'path', 'params',
'comment', 'created_by', 'date_created', 'asset_info', 'comment', 'created_by', 'date_created', 'asset_info',
'system_user_info', 'get_type_display', 'get_type_display',
] ]
read_only_fields = [ read_only_fields = [
'created_by', 'date_created', 'asset_info', 'created_by', 'date_created', 'asset_info',
'system_user_info', 'get_type_display' 'get_type_display'
] ]
@ -89,7 +89,7 @@ class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = RemoteApp model = RemoteApp
fields = [ fields = [
'id', 'name', 'asset', 'system_user', 'parameter_remote_app', 'id', 'name', 'asset', 'parameter_remote_app',
] ]
read_only_fields = ['parameter_remote_app'] read_only_fields = ['parameter_remote_app']

View File

@ -13,7 +13,6 @@
{% csrf_token %} {% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %} {% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.asset layout="horizontal" %} {% bootstrap_field form.asset layout="horizontal" %}
{% bootstrap_field form.system_user layout="horizontal" %}
{% bootstrap_field form.type layout="horizontal" %} {% bootstrap_field form.type layout="horizontal" %}
{% bootstrap_field form.path layout="horizontal" %} {% bootstrap_field form.path layout="horizontal" %}

View File

@ -57,10 +57,6 @@
<td>{% trans 'Asset' %}:</td> <td>{% trans 'Asset' %}:</td>
<td><b><a href="{% url 'assets:asset-detail' pk=remote_app.asset.id %}">{{ remote_app.asset.hostname }}</a></b></td> <td><b><a href="{% url 'assets:asset-detail' pk=remote_app.asset.id %}">{{ remote_app.asset.hostname }}</a></b></td>
</tr> </tr>
<tr>
<td>{% trans 'System user' %}:</td>
<td><b><a href="{% url 'assets:system-user-detail' pk=remote_app.system_user.id %}">{{ remote_app.system_user.name }}</a></b></td>
</tr>
<tr> <tr>
<td>{% trans 'App type' %}:</td> <td>{% trans 'App type' %}:</td>
<td><b>{{ remote_app.get_type_display }}</b></td> <td><b>{{ remote_app.get_type_display }}</b></td>

View File

@ -20,7 +20,6 @@
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'App type' %}</th> <th class="text-center">{% trans 'App type' %}</th>
<th class="text-center">{% trans 'Asset' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
@ -47,12 +46,11 @@ function initTable() {
var detail_btn = '<a href="{% url 'assets:asset-detail' pk=DEFAULT_PK %}">' + hostname + '</a>'; var detail_btn = '<a href="{% url 'assets:asset-detail' pk=DEFAULT_PK %}">' + hostname + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', cellData.id)); $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', cellData.id));
}}, }},
{targets: 4, createdCell: function (td, cellData, rowData) { {targets: 3, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(cellData.name); var comment = htmlEscape(cellData);
var detail_btn = '<a href="{% url 'assets:system-user-detail' pk=DEFAULT_PK %}">' + name + '</a>'; $(td).html(comment)
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', cellData.id));
}}, }},
{targets: 6, createdCell: function (td, cellData, rowData) { {targets: 5, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "applications:remote-app-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData); var update_btn = '<a href="{% url "applications:remote-app-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-rid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-rid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn) $(td).html(update_btn + del_btn)
@ -64,7 +62,6 @@ function initTable() {
{data: "name" }, {data: "name" },
{data: "get_type_display", orderable: false}, {data: "get_type_display", orderable: false},
{data: "asset_info", orderable: false}, {data: "asset_info", orderable: false},
{data: "system_user_info", orderable: false},
{data: "comment"}, {data: "comment"},
{data: "id", orderable: false} {data: "id", orderable: false}
], ],

View File

@ -16,7 +16,6 @@
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'App type' %}</th> <th class="text-center">{% trans 'App type' %}</th>
<th class="text-center">{% trans 'Asset' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
@ -49,11 +48,7 @@ function initTable() {
var hostname = htmlEscape(cellData.hostname); var hostname = htmlEscape(cellData.hostname);
$(td).html(hostname); $(td).html(hostname);
}}, }},
{targets: 4, createdCell: function (td, cellData, rowData) { {targets: 5, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(cellData.name);
$(td).html(name);
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +'" class="btn btn-xs btn-primary">{% trans "Connect" %}</a>'.replace("{{ DEFAULT_PK }}", cellData); var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +'" class="btn btn-xs btn-primary">{% trans "Connect" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
$(td).html(conn_btn) $(td).html(conn_btn)
}} }}
@ -64,7 +59,6 @@ function initTable() {
{data: "name"}, {data: "name"},
{data: "get_type_display", orderable: false}, {data: "get_type_display", orderable: false},
{data: "asset_info", orderable: false}, {data: "asset_info", orderable: false},
{data: "system_user_info", orderable: false},
{data: "comment", orderable: false}, {data: "comment", orderable: false},
{data: "id", orderable: false} {data: "id", orderable: false}
] ]

View File

@ -13,9 +13,7 @@ router = BulkRouter()
router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app') router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app')
urlpatterns = [ urlpatterns = [
path('remote-apps/<uuid:pk>/connection-info/', path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
api.RemoteAppConnectionInfoApi.as_view(),
name='remote-app-connection-info')
] ]
old_version_urlpatterns = [ old_version_urlpatterns = [
re_path('(?P<resource>remote-app)/.*', capi.redirect_plural_name_api) re_path('(?P<resource>remote-app)/.*', capi.redirect_plural_name_api)

View File

@ -9,16 +9,49 @@ from rest_framework.views import Response
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.http import condition from django.views.decorators.http import condition
from rest_framework.generics import get_object_or_404
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from common.utils import get_logger
from assets.utils import LabelFilterMixin from assets.utils import LabelFilterMixin
from .. import const from common.permissions import IsValidUser, IsOrgAdminOrAppUser, IsOrgAdmin
from ..hands import Asset, Node, SystemUser from common.utils import get_logger
from orgs.utils import set_to_root_org
from ..hands import User, Asset, Node, SystemUser
from .. import serializers from .. import serializers
from .. import const
logger = get_logger(__name__) logger = get_logger(__name__)
__all__ = ['UserPermissionCacheMixin', 'GrantAssetsMixin', 'NodesWithUngroupMixin'] __all__ = [
'UserPermissionCacheMixin', 'GrantAssetsMixin', 'NodesWithUngroupMixin',
'UserPermissionMixin',
]
class UserPermissionMixin:
permission_classes = (IsOrgAdminOrAppUser,)
obj = None
def initial(self, *args, **kwargs):
super().initial(*args, *kwargs)
self.obj = self.get_obj()
def get(self, request, *args, **kwargs):
set_to_root_org()
return super().get(request, *args, **kwargs)
def get_obj(self):
user_id = self.kwargs.get('pk', '')
if user_id:
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
return user
def get_permissions(self):
if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
# def get_etag(request, *args, **kwargs): # def get_etag(request, *args, **kwargs):

View File

@ -8,16 +8,16 @@ from rest_framework.generics import (
ListAPIView, get_object_or_404, RetrieveAPIView ListAPIView, get_object_or_404, RetrieveAPIView
) )
from common.permissions import IsValidUser, IsOrgAdminOrAppUser, IsOrgAdmin from common.permissions import IsOrgAdminOrAppUser, IsOrgAdmin
from common.tree import TreeNodeSerializer from common.tree import TreeNodeSerializer
from common.utils import get_logger from common.utils import get_logger
from orgs.utils import set_to_root_org
from ..utils import ( from ..utils import (
ParserNode, AssetPermissionUtilV2 ParserNode, AssetPermissionUtilV2
) )
from ..hands import User, Asset, Node, SystemUser, NodeSerializer from ..hands import User, Asset, Node, SystemUser, NodeSerializer
from .. import serializers from .. import serializers
from ..models import Action from ..models import Action
from .mixin import UserPermissionMixin
logger = get_logger(__name__) logger = get_logger(__name__)
@ -39,32 +39,6 @@ __all__ = [
] ]
class UserPermissionMixin:
permission_classes = (IsOrgAdminOrAppUser,)
obj = None
def initial(self, *args, **kwargs):
super().initial(*args, *kwargs)
self.obj = self.get_obj()
def get(self, request, *args, **kwargs):
set_to_root_org()
return super().get(request, *args, **kwargs)
def get_obj(self):
user_id = self.kwargs.get('pk', '')
if user_id:
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
return user
def get_permissions(self):
if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
class UserAssetPermissionMixin(UserPermissionMixin): class UserAssetPermissionMixin(UserPermissionMixin):
util = None util = None

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import uuid
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework.views import APIView, Response from rest_framework.views import APIView, Response
from rest_framework.generics import ( from rest_framework.generics import (
@ -12,13 +13,16 @@ from ..utils import (
RemoteAppPermissionUtil, construct_remote_apps_tree_root, RemoteAppPermissionUtil, construct_remote_apps_tree_root,
parse_remote_app_to_tree_node, parse_remote_app_to_tree_node,
) )
from ..hands import User, RemoteAppSerializer, UserGroup from ..hands import User, RemoteApp, RemoteAppSerializer, UserGroup, SystemUser
from ..mixins import RemoteAppFilterMixin from ..mixins import RemoteAppFilterMixin
from .mixin import UserPermissionMixin
from .. import serializers
__all__ = [ __all__ = [
'UserGrantedRemoteAppsApi', 'ValidateUserRemoteAppPermissionApi', 'UserGrantedRemoteAppsApi', 'ValidateUserRemoteAppPermissionApi',
'UserGrantedRemoteAppsAsTreeApi', 'UserGroupGrantedRemoteAppsApi', 'UserGrantedRemoteAppsAsTreeApi', 'UserGroupGrantedRemoteAppsApi',
'UserGrantedRemoteAppSystemUsersApi',
] ]
@ -65,18 +69,43 @@ class UserGrantedRemoteAppsAsTreeApi(UserGrantedRemoteAppsApi):
return super().get_serializer(data, many=True) return super().get_serializer(data, many=True)
class UserGrantedRemoteAppSystemUsersApi(UserPermissionMixin, ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.RemoteAppSystemUserSerializer
only_fields = serializers.RemoteAppSystemUserSerializer.Meta.only_fields
def get_queryset(self):
util = RemoteAppPermissionUtil(self.obj)
remote_app_id = self.kwargs.get('remote_app_id')
remote_app = get_object_or_404(RemoteApp, id=remote_app_id)
system_users = util.get_remote_app_system_users(remote_app)
return system_users
class ValidateUserRemoteAppPermissionApi(APIView): class ValidateUserRemoteAppPermissionApi(APIView):
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
user_id = request.query_params.get('user_id', '') user_id = request.query_params.get('user_id', '')
remote_app_id = request.query_params.get('remote_app_id', '') remote_app_id = request.query_params.get('remote_app_id', '')
system_id = request.query_params.get('system_user_id', '')
try:
user_id = uuid.UUID(user_id)
remote_app_id = uuid.UUID(remote_app_id)
system_id = uuid.UUID(system_id)
except ValueError:
return Response({'msg': False}, status=403)
user = get_object_or_404(User, id=user_id) user = get_object_or_404(User, id=user_id)
remote_app = get_object_or_404(RemoteApp, id=remote_app_id)
system_user = get_object_or_404(SystemUser, id=system_id)
util = RemoteAppPermissionUtil(user) util = RemoteAppPermissionUtil(user)
remote_app = util.get_remote_apps().filter(id=remote_app_id).exists() system_users = util.get_remote_app_system_users(remote_app)
if remote_app: if system_user in system_users:
return Response({'msg': True}, status=200) return Response({'msg': True}, status=200)
return Response({'msg': False}, status=403) return Response({'msg': False}, status=403)

View File

@ -35,6 +35,9 @@ class RemoteAppPermissionCreateUpdateForm(OrgModelForm):
), ),
'remote_apps': forms.SelectMultiple( 'remote_apps': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('RemoteApp')} attrs={'class': 'select2', 'data-placeholder': _('RemoteApp')}
),
'system_users': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('System user')}
) )
} }

View File

@ -0,0 +1,32 @@
# Generated by Django 2.1.7 on 2019-09-09 09:09
from django.db import migrations, models
from assets.models import SystemUser
def migrate_system_user_from_remote_app_to_remote_app_perms(apps, schema_editor):
remote_app_perms_model = apps.get_model("perms", "RemoteAppPermission")
db_alias = schema_editor.connection.alias
perms = remote_app_perms_model.objects.using(db_alias).all()
for perm in perms:
system_users_ids = perm.remote_apps.values_list('system_user', flat=True)
perm.system_users.set(system_users_ids)
class Migration(migrations.Migration):
dependencies = [
('assets', '0037_auto_20190724_2002'),
('perms', '0007_remove_assetpermission_actions'),
]
operations = [
migrations.AddField(
model_name='remoteapppermission',
name='system_users',
field=models.ManyToManyField(related_name='granted_by_remote_app_permissions', to='assets.SystemUser', verbose_name='System user'),
),
migrations.RunPython(
code=migrate_system_user_from_remote_app_to_remote_app_perms,
),
]

View File

@ -13,6 +13,7 @@ __all__ = [
class RemoteAppPermission(BasePermission): class RemoteAppPermission(BasePermission):
remote_apps = models.ManyToManyField('applications.RemoteApp', related_name='granted_by_permissions', blank=True, verbose_name=_("RemoteApp")) remote_apps = models.ManyToManyField('applications.RemoteApp', related_name='granted_by_permissions', blank=True, verbose_name=_("RemoteApp"))
system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_remote_app_permissions', verbose_name=_("System user"))
class Meta: class Meta:
unique_together = [('org_id', 'name')] unique_together = [('org_id', 'name')]

View File

@ -20,8 +20,8 @@ class RemoteAppPermissionSerializer(BulkOrgResourceModelSerializer):
model = RemoteAppPermission model = RemoteAppPermission
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = [ fields = [
'id', 'name', 'users', 'user_groups', 'remote_apps', 'comment', 'id', 'name', 'users', 'user_groups', 'remote_apps', 'system_users',
'is_active', 'date_start', 'date_expired', 'is_valid', 'comment', 'is_active', 'date_start', 'date_expired', 'is_valid',
'created_by', 'date_created', 'created_by', 'date_created',
] ]
read_only_fields = ['created_by', 'date_created'] read_only_fields = ['created_by', 'date_created']

View File

@ -12,6 +12,7 @@ __all__ = [
'NodeGrantedSerializer', 'NodeGrantedSerializer',
'AssetGrantedSerializer', 'AssetGrantedSerializer',
'ActionsSerializer', 'AssetSystemUserSerializer', 'ActionsSerializer', 'AssetSystemUserSerializer',
'RemoteAppSystemUserSerializer',
] ]
@ -24,13 +25,22 @@ class AssetSystemUserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = SystemUser model = SystemUser
only_fields = ( only_fields = (
'id', 'name', 'username', 'priority', 'id', 'name', 'username', 'priority', 'protocol', 'login_mode',
'protocol', 'login_mode',
) )
fields = list(only_fields) + ["actions"] fields = list(only_fields) + ["actions"]
read_only_fields = fields read_only_fields = fields
class RemoteAppSystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
only_fields = (
'id', 'name', 'username', 'priority', 'protocol', 'login_mode',
)
fields = list(only_fields)
read_only_fields = fields
class AssetGrantedSerializer(serializers.ModelSerializer): class AssetGrantedSerializer(serializers.ModelSerializer):
""" """
被授权资产的数据结构 被授权资产的数据结构

View File

@ -47,6 +47,7 @@
<h3>{% trans 'RemoteApp' %}</h3> <h3>{% trans 'RemoteApp' %}</h3>
{% bootstrap_field form.remote_apps layout="horizontal" %} {% bootstrap_field form.remote_apps layout="horizontal" %}
{% bootstrap_field form.system_users layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
@ -127,7 +128,7 @@ $(document).ready(function () {
the_url = '{% url "api-perms:remote-app-permission-detail" pk=object.id %}'; the_url = '{% url "api-perms:remote-app-permission-detail" pk=object.id %}';
method = "PUT"; method = "PUT";
{% endif %} {% endif %}
objectAttrsIsList(data, ['users', 'user_groups', 'remote_apps']); objectAttrsIsList(data, ['users', 'user_groups', 'remote_apps', 'system_users']);
objectAttrsIsDatetime(data, ['date_expired', 'date_start']); objectAttrsIsDatetime(data, ['date_expired', 'date_start']);
objectAttrsIsBool(data, ['is_active']); objectAttrsIsBool(data, ['is_active']);
var props = { var props = {

View File

@ -126,7 +126,42 @@
</table> </table>
</div> </div>
</div> </div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'System user' %}
</div>
<div class="panel-body">
<table class="table" id="system-user-table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select system users' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for system_user in system_users_remain %}
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-info btn-small" id="btn-add-system-user">{% trans 'Add' %}</button>
</td>
</tr>
</form>
{% for system_user in object.system_users.all %}
<tr {% if forloop.counter == 1 %} class="no-borders-tr" {% endif %} >
<td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user }}</b></td>
<td>
<button class="btn btn-danger btn-xs btn-remove-user" data-uid="{{ system_user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -136,6 +171,20 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
jumpserver.system_users_selected = {};
function updateSystemUser(system_users) {
var the_url = "{% url 'api-perms:remote-app-permission-detail' pk=object.id %}";
var body = {
system_users: Object.assign([], system_users)
};
requestApi({
url: the_url,
body: JSON.stringify(body),
success: function () {window.location.reload()}
});
}
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2() $('.select2').select2()
.on('select2:select', function(evt) { .on('select2:select', function(evt) {
@ -147,7 +196,36 @@ $(document).ready(function () {
delete jumpserver.system_users_selected[data.id] delete jumpserver.system_users_selected[data.id]
}) })
}) })
.on('click', '.btn-delete', function () { .on('click', '#btn-add-system-user', function () {
if (Object.keys(jumpserver.system_users_selected).length === 0) {
return false;
}
var system_users = $('.bdg-system-user').map(function() {
return $(this).data('uid');
}).get();
$.map(jumpserver.system_users_selected, function(name, index) {
system_users.push(index);
$('#opt_' + index).remove();
$('.group_edit tbody').append(
'<tr>' +
'<td><b class="bdg-system-user" data-gid="' + index + '">' + name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn-remove-user" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
updateSystemUser(system_users);
}).on('click', '.btn-remove-user', function () {
var $this = $(this);
var $tr = $this.closest('tr');
var system_users = $('.bdg-system-user').map(function() {
if ($(this).data('uid') !== $this.data('uid')){
return $(this).data('uid');
}
}).get();
updateSystemUser(system_users);
$tr.remove()
}).on('click', '.btn-delete', function () {
var $this = $(this); var $this = $(this);
var name = "{{ object.name }}"; var name = "{{ object.name }}";
var rid = "{{ object.id }}"; var rid = "{{ object.id }}";

View File

@ -91,6 +91,10 @@ remote_app_permission_urlpatterns = [
# 查询用户组授权的RemoteApp # 查询用户组授权的RemoteApp
path('user-groups/<uuid:pk>/remote-apps/', api.UserGroupGrantedRemoteAppsApi.as_view(), name='user-group-remote-apps'), path('user-groups/<uuid:pk>/remote-apps/', api.UserGroupGrantedRemoteAppsApi.as_view(), name='user-group-remote-apps'),
# RemoteApp System users
path('users/<uuid:pk>/remote-apps/<uuid:remote_app_id>/system-users/', api.UserGrantedRemoteAppSystemUsersApi.as_view(), name='user-remote-app-system-users'),
path('users/remote-apps/<uuid:remote_app_id>/system-users/', api.UserGrantedRemoteAppSystemUsersApi.as_view(), name='my-remote-app-system-users'),
# 校验用户对RemoteApp的权限 # 校验用户对RemoteApp的权限
path('remote-app-permissions/user/validate/', api.ValidateUserRemoteAppPermissionApi.as_view(), name='validate-user-remote-app-permission'), path('remote-app-permissions/user/validate/', api.ValidateUserRemoteAppPermissionApi.as_view(), name='validate-user-remote-app-permission'),

View File

@ -7,7 +7,7 @@ from common.tree import TreeNode
from orgs.utils import set_to_root_org from orgs.utils import set_to_root_org
from ..models import RemoteAppPermission from ..models import RemoteAppPermission
from ..hands import RemoteApp from ..hands import RemoteApp, SystemUser
__all__ = [ __all__ = [
@ -59,6 +59,16 @@ class RemoteAppPermissionUtil:
) )
return remote_apps return remote_apps
def get_remote_app_system_users(self, remote_app):
queryset = self.permissions
kwargs = {"remote_apps": remote_app}
queryset = queryset.filter(**kwargs)
system_users_ids = queryset.values_list('system_users', flat=True)
system_users_ids = system_users_ids.distinct()
system_users = SystemUser.objects.filter(id__in=system_users_ids)
system_users = system_users.order_by('-priority')
return system_users
def construct_remote_apps_tree_root(): def construct_remote_apps_tree_root():
tree_root = { tree_root = {

View File

@ -12,7 +12,7 @@ from django.conf import settings
from common.permissions import PermissionsMixin, IsOrgAdmin from common.permissions import PermissionsMixin, IsOrgAdmin
from orgs.utils import current_org from orgs.utils import current_org
from ..hands import RemoteApp, UserGroup from ..hands import RemoteApp, UserGroup, SystemUser
from ..models import RemoteAppPermission from ..models import RemoteAppPermission
from ..forms import RemoteAppPermissionCreateUpdateForm from ..forms import RemoteAppPermissionCreateUpdateForm
@ -80,6 +80,9 @@ class RemoteAppPermissionDetailView(PermissionsMixin, DetailView):
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('RemoteApp permission detail'), 'action': _('RemoteApp permission detail'),
'system_users_remain': SystemUser.objects.exclude(
granted_by_remote_app_permissions=self.object
),
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)