[Update] 修改用户详情页面 (#3555)

* [Update] 用户详情添加远程应用授权页面

* [Update] 用户详情添加授权的远程应用页面

* [Update] 用户详情添加授权的数据库应用页面

* [Update] 用户详情添加数据库应用授权页面

* [Update] 修改用户详情nav的active属性设置

* [Update] 修改用户详情页面导航

* [Update] 抽象用户详情页面

* [Update] 修改用户详情页面

* [Update] 修改用户详情页面nav header
pull/3556/head
BaiJiangJie 2019-12-20 15:55:59 +08:00 committed by GitHub
parent b365ba7982
commit 829e1f4cac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1126 additions and 433 deletions

View File

@ -65,7 +65,7 @@ function initTable() {
{data: "port"}, {data: "port"},
{data: "database"}, {data: "database"},
{data: "comment"}, {data: "comment"},
{data: "id", orderable: false, width: "100px"} {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -69,7 +69,7 @@ function initTable() {
{data: "get_type_display", orderable: false}, {data: "get_type_display", orderable: false},
{data: "asset_info", orderable: false}, {data: "asset_info", orderable: false},
{data: "comment"}, {data: "comment"},
{data: "id", orderable: false, width: "100px"} {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -51,7 +51,7 @@ function initTable() {
columns: [ columns: [
{data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount", orderable: false}, {data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount", orderable: false},
{#{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"},#} {#{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"},#}
{data: "comment"}, {data: "id", orderable: false, width: "100px"} {data: "comment"}, {data: "id", orderable: false, width: "120px"}
] ]
}; };
return jumpserver.initServerSideDataTable(options); return jumpserver.initServerSideDataTable(options);

View File

@ -99,7 +99,7 @@ function initTable() {
data: "connectivity", data: "connectivity",
orderable: false, orderable: false,
width: '60px' width: '60px'
}, {data: "id", orderable: false, width: "100px"} }, {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -62,7 +62,7 @@ function initTable() {
columns: [ columns: [
{data: "id"}, {data: "name" }, {data: "rules", orderable: false}, {data: "id"}, {data: "name" }, {data: "rules", orderable: false},
{data: "system_users", orderable: false}, {data: "comment"}, {data: "system_users", orderable: false}, {data: "comment"},
{data: "id", orderable: false, width: "100px"} {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -55,7 +55,7 @@ function initTable() {
ajax_url: '{% url "api-assets:domain-list" %}', ajax_url: '{% url "api-assets:domain-list" %}',
columns: [ columns: [
{data: "id"}, {data: "name" }, {data: "asset_count", orderable: false }, {data: "id"}, {data: "name" }, {data: "asset_count", orderable: false },
{data: "gateway_count", orderable: false }, {data: "comment" }, {data: "id", orderable: false, width: "100px"} {data: "gateway_count", orderable: false }, {data: "comment" }, {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -45,7 +45,7 @@ function initTable() {
columns: [ columns: [
{data: "id"}, {data: "name" }, {data: "value" }, {data: "id"}, {data: "name" }, {data: "value" },
{data: "asset_count", orderable: false}, {data: "asset_count", orderable: false},
{data: "id", orderable: false, width: "100px"} {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -50,7 +50,7 @@ function initTable() {
ajax_url: '{% url "api-assets:platform-list" %}', ajax_url: '{% url "api-assets:platform-list" %}',
columns: [ columns: [
{data: "id"}, {data: "name"}, {data: "base" }, {data: "id"}, {data: "name"}, {data: "base" },
{data: "comment"}, {data: "id", orderable: false, width: "100px"} {data: "comment"}, {data: "id", orderable: false, width: "120px"}
] ]
}; };
platformTable = jumpserver.initServerSideDataTable(options); platformTable = jumpserver.initServerSideDataTable(options);

View File

@ -62,7 +62,7 @@ function initTable() {
columns: [ columns: [
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"},
{data: "login_mode"}, {data: "assets_amount", orderable: false }, {data: "login_mode"}, {data: "assets_amount", orderable: false },
{data: "comment" }, {data: "id", orderable: false, width: "100px"} {data: "comment" }, {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -62,7 +62,7 @@ function initAccessKeyTable() {
}}, }},
{targets: 5, createdCell: function (td, cellData, rowData) { {targets: 5, createdCell: function (td, cellData, rowData) {
var btn = ''; var btn = '';
var btn_del = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-id="ID">{% trans "Delete" %}</a>'; var btn_del = '<a class="btn btn-xs btn-danger m-l-xs btn-api-keydel" data-id="ID">{% trans "Delete" %}</a>';
var btn_inactive = '<a class="btn btn-xs btn-info m-l-xs btn-inactive" data-id="ID">{% trans "Disable" %}</a>'; var btn_inactive = '<a class="btn btn-xs btn-info m-l-xs btn-inactive" data-id="ID">{% trans "Disable" %}</a>';
var btn_active = '<a class="btn btn-xs btn-primary m-l-xs btn-active" data-id="ID">{% trans "Enable" %}</a>'; var btn_active = '<a class="btn btn-xs btn-primary m-l-xs btn-active" data-id="ID">{% trans "Enable" %}</a>';
@ -107,7 +107,7 @@ $(document).ready(function () {
}).on("click", ".btn-secret", function () { }).on("click", ".btn-secret", function () {
var $this = $(this); var $this = $(this);
$this.parent().html($this.data("secret")) $this.parent().html($this.data("secret"))
}).on("click", ".btn-del", function () { }).on("click", ".btn-api-key-del", function () {
var url = "{% url "api-auth:access-key-detail" pk=DEFAULT_PK %}"; var url = "{% url "api-auth:access-key-detail" pk=DEFAULT_PK %}";
url = url.replace("{{ DEFAULT_PK }}", $(this).data("id")) ; url = url.replace("{{ DEFAULT_PK }}", $(this).data("id")) ;
objectDelete($(this), $(this).data("id"), url); objectDelete($(this), $(this).data("id"), url);

View File

@ -12,7 +12,10 @@ __all__ = ['DatabaseAppPermissionViewSet']
class DatabaseAppPermissionViewSet(OrgBulkModelViewSet): class DatabaseAppPermissionViewSet(OrgBulkModelViewSet):
model = models.DatabaseAppPermission model = models.DatabaseAppPermission
serializer_class = serializers.DatabaseAppPermissionSerializer serializer_classes = {
'default': serializers.DatabaseAppPermissionSerializer,
'display': serializers.DatabaseAppPermissionListSerializer
}
filter_fields = ('name',) filter_fields = ('name',)
search_fields = filter_fields search_fields = filter_fields
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)

View File

@ -11,6 +11,7 @@ from ..serializers import (
RemoteAppPermissionSerializer, RemoteAppPermissionSerializer,
RemoteAppPermissionUpdateUserSerializer, RemoteAppPermissionUpdateUserSerializer,
RemoteAppPermissionUpdateRemoteAppSerializer, RemoteAppPermissionUpdateRemoteAppSerializer,
RemoteAppPermissionListSerializer,
) )
@ -25,7 +26,10 @@ class RemoteAppPermissionViewSet(OrgModelViewSet):
model = RemoteAppPermission model = RemoteAppPermission
filter_fields = ('name', ) filter_fields = ('name', )
search_fields = filter_fields search_fields = filter_fields
serializer_class = RemoteAppPermissionSerializer serializer_classes = {
'default': RemoteAppPermissionSerializer,
'display': RemoteAppPermissionListSerializer,
}
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)

View File

@ -1,11 +1,16 @@
# coding: utf-8 # coding: utf-8
# #
from rest_framework import serializers
from common.fields import StringManyToManyField
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .. import models from .. import models
__all__ = ['DatabaseAppPermissionSerializer'] __all__ = [
'DatabaseAppPermissionSerializer', 'DatabaseAppPermissionListSerializer'
]
class DatabaseAppPermissionSerializer(BulkOrgResourceModelSerializer): class DatabaseAppPermissionSerializer(BulkOrgResourceModelSerializer):
@ -19,3 +24,16 @@ class DatabaseAppPermissionSerializer(BulkOrgResourceModelSerializer):
'created_by', 'date_created' 'created_by', 'date_created'
] ]
read_only_fields = ['created_by', 'date_created'] read_only_fields = ['created_by', 'date_created']
class DatabaseAppPermissionListSerializer(BulkOrgResourceModelSerializer):
users = StringManyToManyField(many=True, read_only=True)
user_groups = StringManyToManyField(many=True, read_only=True)
database_apps = StringManyToManyField(many=True, read_only=True)
system_users = StringManyToManyField(many=True, read_only=True)
is_valid = serializers.BooleanField()
is_expired = serializers.BooleanField()
class Meta:
model = models.DatabaseAppPermission
fields = '__all__'

View File

@ -3,6 +3,7 @@
from rest_framework import serializers from rest_framework import serializers
from common.fields import StringManyToManyField
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import RemoteAppPermission from ..models import RemoteAppPermission
@ -12,6 +13,7 @@ __all__ = [
'RemoteAppPermissionSerializer', 'RemoteAppPermissionSerializer',
'RemoteAppPermissionUpdateUserSerializer', 'RemoteAppPermissionUpdateUserSerializer',
'RemoteAppPermissionUpdateRemoteAppSerializer', 'RemoteAppPermissionUpdateRemoteAppSerializer',
'RemoteAppPermissionListSerializer',
] ]
@ -27,6 +29,19 @@ class RemoteAppPermissionSerializer(BulkOrgResourceModelSerializer):
read_only_fields = ['created_by', 'date_created'] read_only_fields = ['created_by', 'date_created']
class RemoteAppPermissionListSerializer(BulkOrgResourceModelSerializer):
users = StringManyToManyField(many=True, read_only=True)
user_groups = StringManyToManyField(many=True, read_only=True)
remote_apps = StringManyToManyField(many=True, read_only=True)
system_users = StringManyToManyField(many=True, read_only=True)
is_valid = serializers.BooleanField()
is_expired = serializers.BooleanField()
class Meta:
model = RemoteAppPermission
fields = '__all__'
class RemoteAppPermissionUpdateUserSerializer(serializers.ModelSerializer): class RemoteAppPermissionUpdateUserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = RemoteAppPermission model = RemoteAppPermission

View File

@ -156,7 +156,7 @@ function initTable() {
{data: "id"}, {data: "name"}, {data: "users", orderable: false}, {data: "id"}, {data: "name"}, {data: "users", orderable: false},
{data: "user_groups", orderable: false}, {data: "assets", orderable: false}, {data: "user_groups", orderable: false}, {data: "assets", orderable: false},
{data: "nodes", orderable: false}, {data: "system_users", orderable: false}, {data: "nodes", orderable: false}, {data: "system_users", orderable: false},
{data: "is_valid", orderable: false}, {data: "id", orderable: false, width: "100px"} {data: "is_valid", orderable: false}, {data: "id", orderable: false, width: "120px"}
], ],
select: {}, select: {},
op_html: $('#actions').html() op_html: $('#actions').html()

View File

@ -75,7 +75,7 @@ function initTable() {
{data: "database_apps", orderable: false}, {data: "database_apps", orderable: false},
{data: "system_users", orderable: false}, {data: "system_users", orderable: false},
{data: "is_valid", orderable: false}, {data: "is_valid", orderable: false},
{data: "id", orderable: false, width: "100px"} {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -75,7 +75,7 @@ function initTable() {
{data: "remote_apps", orderable: false}, {data: "remote_apps", orderable: false},
{data: "system_users", orderable: false}, {data: "system_users", orderable: false},
{data: "is_valid", orderable: false}, {data: "is_valid", orderable: false},
{data: "id", orderable: false, width: "100px"} {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -158,7 +158,7 @@ function activeNav(prefix) {
} else { } else {
$("#" + app).addClass('active'); $("#" + app).addClass('active');
$('#' + app + ' #' + resource).addClass('active'); $('#' + app + ' #' + resource).addClass('active');
$('#' + app + ' #' + resource.replaceAll('-', '_')).addClass('active'); $('#' + app + ' #' + resource.replace(/-/g, '_')).addClass('active');
} }
} }

View File

@ -0,0 +1,26 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
{% include 'users/_user_detail_nav_header.html' %}
{% block content_nav_delete_update %}
{% endblock %}
</ul>
</div>
<div class="tab-content">
{% block content_table %}
{% endblock %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,97 @@
{% load static %}
{% load i18n %}
<style>
.nav .open>a, .nav .open>a:hover, .nav .open>a:focus{
border-color: white;
}
</style>
<li id="id_nav_user_detail">
<a href="{% url 'users:user-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
</li>
<li id="id_nav_user_detail_user_permission" class="btn-group">
<a class="btn btn-sm dropdown-toggle" data-toggle="dropdown">
<span>{% trans "User permissions" %}</span>
<i class="caret"></i>
</a>
<ul class="dropdown-menu">
<li id="id_nav_user_detail_assets">
<a href="{% url 'users:user-granted-asset' pk=object.id %}" class="text-center">
<i class="fa fa-cubes"></i>
<span>{% trans 'Asset granted' %}</span>
<i class="highlight-circle"></i>
</a>
</li>
<li id="id_nav_user_detail_asset_permissions">
<a href="{% url 'users:user-asset-permission' pk=object.id %}" class="text-center">
<i class="fa fa-edit"></i>
<span>{% trans 'Asset permission' %}</span>
<i class="highlight-circle"></i>
</a>
</li>
{% if LICENSE_VALID %}
<li id="id_nav_user_detail_remote_apps">
<a href="{% url 'users:user-granted-remote-app' pk=object.id %}" class="text-center">
<i class="fa fa-desktop"></i>
<span>{% trans 'RemoteApp granted' %}</span>
<i class="highlight-circle"></i>
</a>
</li>
<li id="id_nav_user_detail_remote_app_permissions">
<a href="{% url 'users:user-remote-app-permission' pk=object.id %}" class="text-center">
<i class="fa fa-edit"></i>
<span>{% trans 'RemoteApp permission' %}</span>
<i class="highlight-circle"></i>
</a>
</li>
<li id="id_nav_user_detail_database_apps">
<a href="{% url 'users:user-granted-database-app' pk=object.id %}" class="text-center">
<i class="fa fa-database"></i>
<span>{% trans 'DatabaseApp granted' %}</span>
<i class="highlight-circle"></i>
</a>
</li>
<li id="id_nav_user_detail_database_app_permissions">
<a href="{% url 'users:user-database-app-permission' pk=object.id %}" class="text-center">
<i class="fa fa-edit"></i>
<span>{% trans 'DatabaseApp permission' %}</span>
<i class="highlight-circle"></i>
</a>
</li>
{% endif %}
</ul>
</li>
<script>
function activeUserDetailNav(prefix) {
var path = document.location.pathname;
if (prefix) {
path = path.replace(prefix, '');
console.log(path);
}
var navId, navId2 = '';
var idPrefix = 'id_nav_user_detail';
var navUserDetailId = "#" + idPrefix;
var navUserPermissionId = "#" + idPrefix + "_user_permission";
var urlArray = path.split("/");
var page = urlArray[urlArray.length-2];
if (page === "{{ object.id }}" || page === undefined){
navId = navUserDetailId;
}
else{
navId = navUserPermissionId;
navId2 = "#" + idPrefix + '_' + page.replace(/-/g, '_');
var highlightCircle = '<span class="fa fa-circle" style="padding-left: 5px; color: #1ab394"></span>';
$(navId2 + '>a>i.highlight-circle').html(highlightCircle);
$(navId + '>a>span').html($(navId2 + '>a>span').html());
}
$(navId).addClass('active')
}
activeUserDetailNav("{{ FORCE_SCRIPT_NAME }}");
</script>

View File

@ -1,4 +1,4 @@
{% extends 'base.html' %} {% extends 'users/_base_user_detail.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
@ -6,75 +6,55 @@
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row"> {% block content_table %}
<div class="col-sm-12"> <div class="col-sm-10" style="padding-left: 0">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="panel-options"> <div class="ibox-title">
<ul class="nav nav-tabs"> <span class="label"><b>{{ object.name }}</b></span>
<li> <div class="ibox-tools">
<a href="{% url 'users:user-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a> <a class="collapse-link">
</li> <i class="fa fa-chevron-up"></i>
<li> </a>
<a href="{% url 'users:user-granted-asset' pk=object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a> <a class="dropdown-toggle" data-toggle="dropdown" href="#">
</li> <i class="fa fa-wrench"></i>
<li class="active"> </a>
<a href="{% url 'users:user-asset-permission' pk=object.id %}" class="text-center"><i class="fa fa-edit"></i> {% trans 'Asset permission' %}</a> <ul class="dropdown-menu dropdown-user">
</li> </ul>
</ul> <a class="close-link">
</div> <i class="fa fa-times"></i>
<div class="tab-content"> </a>
<div class="col-sm-10" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="mail-box-header">
<table class="table table-striped table-bordered table-hover"
id="permission_list_table"
style="width: 100%">
<thead>
<tr>
<th></th>
<th>{% trans 'Name' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Node' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Validity' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="ibox-content">
<table class="table table-striped table-bordered table-hover"
id="permission_list_table"
style="width: 100%">
<thead>
<tr>
<th></th>
<th>{% trans 'Name' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Node' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Validity' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div> </div>
{% include '_filter_dropdown.html' %} </div>
{% include '_filter_dropdown.html' %}
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
jumpserver.nodes_selected = {}; jumpserver.nodes_selected = {};
@ -159,7 +139,7 @@ function initTable() {
{data: "id"}, {data: "name"}, {data: "users", orderable: false}, {data: "id"}, {data: "name"}, {data: "users", orderable: false},
{data: "user_groups", orderable: false}, {data: "assets", orderable: false}, {data: "user_groups", orderable: false}, {data: "assets", orderable: false},
{data: "nodes", orderable: false}, {data: "system_users", orderable: false}, {data: "nodes", orderable: false}, {data: "system_users", orderable: false},
{data: "is_valid", orderable: false}, {data: "id", orderable: false, width: "100px"} {data: "is_valid", orderable: false}, {data: "id", orderable: false, width: "120px"}
], ],
select: {}, select: {},
op_html: $('#actions').html() op_html: $('#actions').html()
@ -184,19 +164,6 @@ $(document).ready(function() {
]; ];
initTableFilterDropdown('#permission_list_table_filter input', filterMenu) initTableFilterDropdown('#permission_list_table_filter input', filterMenu)
}) })
.on('click', '#is_active', function() {
var the_url = "{% url 'api-users:user-detail' pk=object.id %}";
var checked = $(this).prop('checked');
var body = {
'is_active': checked
};
var success = '{% trans "Update successfully!" %}';
requestApi({
url: the_url,
body: JSON.stringify(body),
success_message: success
});
})
.on('click', '.toggle', function (e) { .on('click', '.toggle', function (e) {
e.preventDefault(); e.preventDefault();
var detailRows = []; var detailRows = [];
@ -222,5 +189,13 @@ $(document).ready(function() {
} }
} }
}) })
.on('click', '.btn-del', function () {
var $this = $(this);
var uid = $this.data('uid');
var name = $this.data('name');
var the_url = '{% url "api-perms:asset-permission-detail" pk=DEFAULT_PK %}'
.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
})
</script> </script>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,168 @@
{% extends 'users/_base_user_detail.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content_table %}
<div class="col-sm-10" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-striped table-bordered table-hover"
id="permission_list_table"
style="width: 100%">
<thead>
<tr>
<th></th>
<th>{% trans 'Name' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'DatabaseApp' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Validity' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
function format(d) {
var data = "";
if (d.users.length > 0 ) {
data += makeLabel(["{% trans 'User' %}", d.users.join(", ")])
}
if (d.user_groups.length > 0) {
data += makeLabel(["{% trans 'User group' %}", d.user_groups.join(", ")])
}
if (d.database_apps.length > 0) {
data += makeLabel(["{% trans 'DatabaseApp' %}", d.database_apps.join(", ")])
}
if (d.system_users.length > 0) {
data += makeLabel(["{% trans 'System user' %}", d.system_users.join(", ")])
}
return data
}
function initTable() {
var options = {
ele: $('#permission_list_table'),
toggle: true,
columnDefs: [
{targets: 0, createdCell: function (td, cellData, rowData) {
$(td).addClass("toggle");
$(td).html("<i class='fa fa-angle-right'></i>");
}},
{targets: 1, createdCell: function (td, cellData, rowData) {
cellData = htmlEscape(cellData);
var detail_btn = '<a href="{% url "perms:database-app-permission-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 3, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 4, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 5, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 6, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(rowData.name);
var update_btn = '<a href="{% url "perms:database-app-permission-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-uid="{{ DEFAULT_PK }}" mark=1 data-name="99991938">{% trans "Delete" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', name);
$(td).html(update_btn + del_btn);
}}
],
ajax_url: '{% url "api-perms:database-app-permission-list" %}?user_id={{ object.id }}',
columns: [
{data: "id"}, {data: "name"}, {data: "users", orderable: false},
{data: "user_groups", orderable: false}, {data: "database_apps", orderable: false},
{data: "system_users", orderable: false}, {data: "is_valid", orderable: false},
{data: "id", orderable: false, width: "120px"}
],
select: {},
op_html: $('#actions').html()
};
table = jumpserver.initServerSideDataTable(options);
return table
}
$(document).ready(function() {
initTable();
})
.on('click', '.toggle', function (e) {
e.preventDefault();
var detailRows = [];
var tr = $(this).closest('tr');
var row = table.row(tr);
var idx = $.inArray(tr.attr('id'), detailRows);
if (row.child.isShown()) {
tr.removeClass('details');
$(this).children('i:first-child').removeClass('fa-angle-down').addClass('fa-angle-right');
row.child.hide();
// Remove from the 'open' array
detailRows.splice(idx, 1);
}
else {
tr.addClass('details');
$(this).children('i:first-child').removeClass('fa-angle-right').addClass('fa-angle-down');
row.child(format(row.data())).show();
// Add to the 'open' array
if ( idx === -1 ) {
detailRows.push(tr.attr('id'));
}
}
})
.on('click', '.btn-del', function () {
var $this = $(this);
var uid = $this.data('uid');
var name = $this.data('name');
var the_url = '{% url "api-perms:database-app-permission-detail" pk=DEFAULT_PK %}'
.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
})
</script>
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends 'base.html' %} {% extends 'users/_base_user_detail.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
@ -6,311 +6,296 @@
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'users:user-detail' pk=user_object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
</li>
<li>
<a href="{% url 'users:user-granted-asset' pk=user_object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
</li>
<li>
<a href="{% url 'users:user-asset-permission' pk=user_object.id %}" class="text-center"><i class="fa fa-edit"></i> {% trans 'Asset permission' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline {% if can_update %} btn-default {% else %} disabled {% endif %}" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right"> {% block content_nav_delete_update %}
<a class="btn btn-outline {% if can_delete %} btn-danger btn-delete-user {% else %} disabled {% endif %}"> <li class="pull-right">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %} <a class="btn btn-outline {% if can_update %} btn-default {% else %} disabled {% endif %}" href="{% url 'users:user-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</a> </li>
</li> <li class="pull-right">
</ul> <a class="btn btn-outline {% if can_delete %} btn-danger btn-delete-user {% else %} disabled {% endif %}">
</div> <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
<div class="tab-content"> </a>
<div class="col-sm-8" style="padding-left: 0"> </li>
<div class="ibox float-e-margins"> {% endblock %}
<div class="ibox-title">
<span class="label"><b>{{ user_object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td colspan="2">
<img src="{{ user_object.avatar_url }}" class="img-circle" width="64" height="64">
</td>
</tr>
<tr>
<td width="20%">{% trans 'Name' %}:</td>
<td><b>{{ user_object.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Username' %}:</td>
<td><b>{{ user_object.username }}</b></td>
</tr>
<tr>
<td>{% trans 'Email' %}:</td>
<td><b>{{ user_object.email }}</b></td>
</tr>
{% if user.phone %}
<tr>
<td>{% trans 'Phone' %}:</td>
<td><b>{{ user_object.phone }}</b></td>
</tr>
{% endif %}
{% if user_object.wechat %}
<tr>
<td>{% trans 'Wechat' %}:</td>
<td><b>{{ user_object.wechat }}</b></td>
</tr>
{% endif %}
<tr>
<td>{% trans 'Role' %}:</td>
<td><b>{{ user_object.role_display }}</b></td>
</tr>
<tr>
<td>{% trans 'MFA certification' %}:</td>
<td><b>
{% if user_object.mfa_force_enabled %}
{% trans 'Force enabled' %}
{% elif user_object.mfa_enabled%}
{% trans 'Enabled' %}
{% else %}
{% trans 'Disabled' %}
{% endif %}
</b></td>
</tr>
<tr>
<td>{% trans 'Source' %}:</td>
<td><b>{{ user_object.get_source_display }}</b></td>
</tr>
<tr>
<td>{% trans 'Date expired' %}:</td>
<td><b>{{ user_object.date_expired|date:"Y-m-j H:i:s" }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ user_object.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Date joined' %}:</td>
<td><b>{{ user_object.date_joined|date:"Y-m-j H:i:s" }}</b></td>
</tr>
<tr>
<td>{% trans 'Last login' %}:</td>
<td><b>{{ user_object.last_login|date:"Y-m-j H:i:s" }}</b></td>
</tr>
{% if user_object.can_update_password %}
<tr>
<td>{% trans 'Last password updated' %}:</td>
<td><b>{{ user_object.date_password_last_updated|date:"Y-m-j H:i:s" }}</b></td>
</tr>
{% endif %}
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ user_object.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-4" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Active' %}:</td>
<td><span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" {% if user_object.is_active %} checked {% endif %} {% if request.user == user_object %} disabled {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
<tr>
<td>{% trans 'Force enabled MFA' %}:</td>
<td>
<span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.mfa_force_enabled %} checked {% endif %}
id="force_enable_mfa">
<label class="onoffswitch-label" for="force_enable_mfa">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Reset MFA' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn-reset-mfa" style="width: 54px">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
{% if user_object.can_update_password %}
<tr>
<td>{% trans 'Send reset password mail' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" {% if request.user == user_object %} disabled="disabled" {% endif %} id="btn-reset-password" style="width: 54px">{% trans 'Send' %}</button>
</span>
</td>
</tr>
{% endif %}
{% if user_object.can_update_ssh_key %}
<tr>
<td>{% trans 'Send reset ssh key mail' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" {% if request.user == user_object %} disabled="disabled" {% endif %} id="btn-reset-pk" style="width: 54px;">{% trans 'Send' %}</button>
</span>
</td>
</tr>
{% endif %}
<tr style="{% if not unblock %}display:none{% endif %}">
<td>{% trans 'Unblock user' %}</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn-unblock-user" style="width: 54px">{% trans 'Unblock' %}</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
{% if request.user.can_admin_current_org %}
{% if user_object.can_user_current_org or user_object.can_admin_current_org %} {% block content_table %}
<div class="panel panel-info"> <div class="col-sm-8" style="padding-left: 0">
<div class="panel-heading"> <div class="ibox float-e-margins">
<i class="fa fa-info-circle"></i> {% trans 'User group' %} <div class="ibox-title">
</div> <span class="label"><b>{{ object.name }}</b></span>
<div class="panel-body"> <div class="ibox-tools">
<table class="table group_edit"> <a class="collapse-link">
<tbody> <i class="fa fa-chevron-up"></i>
<form> </a>
<tr> <a class="dropdown-toggle" data-toggle="dropdown" href="#">
<td colspan="2" class="no-borders"> <i class="fa fa-wrench"></i>
<select data-placeholder="{% trans 'Join user groups' %}" id="groups_selected" class="select2" style="width: 100%" multiple="" tabindex="4"> </a>
{% for group in groups %} <ul class="dropdown-menu dropdown-user">
<option value="{{ group.id }}" id="opt_{{ group.id }}" >{{ group.name }}</option> </ul>
{% endfor %} <a class="close-link">
</select> <i class="fa fa-times"></i>
</td> </a>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_join_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in user_object.groups.all %}
<tr>
<td >
<b class="bdg_group" >{{ group.name }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" data-uid={{ group.id }} type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if LICENSE_VALID and LOGIN_CONFIRM_ENABLE %}
<div class="panel panel-warning">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Login confirm' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Reviewers' %}" id="id_assignees" class="users-select2" style="width: 100%" multiple="" tabindex="4">
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-warning btn-small" id="btn_reviewer_confirm">{% trans 'Confirm' %}</button>
</td>
</tr>
</form>
{% if user_object.get_login_confirm_setting %}
{% for u in user_object.login_confirm_setting.reviewers.all %}
<tr>
<td >
<b class="bdg_reviewer">{{ u }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn-leave-reviewer" data-uid={{ u.id }} type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% endif %}
</div>
</div>
</div> </div>
</div> </div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td colspan="2">
<img src="{{ object.avatar_url }}" class="img-circle" width="64" height="64">
</td>
</tr>
<tr>
<td width="20%">{% trans 'Name' %}:</td>
<td><b>{{ object.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Username' %}:</td>
<td><b>{{ object.username }}</b></td>
</tr>
<tr>
<td>{% trans 'Email' %}:</td>
<td><b>{{ object.email }}</b></td>
</tr>
{% if user.phone %}
<tr>
<td>{% trans 'Phone' %}:</td>
<td><b>{{ object.phone }}</b></td>
</tr>
{% endif %}
{% if object.wechat %}
<tr>
<td>{% trans 'Wechat' %}:</td>
<td><b>{{ object.wechat }}</b></td>
</tr>
{% endif %}
<tr>
<td>{% trans 'Role' %}:</td>
<td><b>{{ object.role_display }}</b></td>
</tr>
<tr>
<td>{% trans 'MFA certification' %}:</td>
<td><b>
{% if object.mfa_force_enabled %}
{% trans 'Force enabled' %}
{% elif object.mfa_enabled%}
{% trans 'Enabled' %}
{% else %}
{% trans 'Disabled' %}
{% endif %}
</b></td>
</tr>
<tr>
<td>{% trans 'Source' %}:</td>
<td><b>{{ object.get_source_display }}</b></td>
</tr>
<tr>
<td>{% trans 'Date expired' %}:</td>
<td><b>{{ object.date_expired|date:"Y-m-j H:i:s" }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ object.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Date joined' %}:</td>
<td><b>{{ object.date_joined|date:"Y-m-j H:i:s" }}</b></td>
</tr>
<tr>
<td>{% trans 'Last login' %}:</td>
<td><b>{{ object.last_login|date:"Y-m-j H:i:s" }}</b></td>
</tr>
{% if object.can_update_password %}
<tr>
<td>{% trans 'Last password updated' %}:</td>
<td><b>{{ object.date_password_last_updated|date:"Y-m-j H:i:s" }}</b></td>
</tr>
{% endif %}
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ object.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div> </div>
</div> </div>
{% include 'users/_user_update_pk_modal.html' %} <div class="col-sm-4" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Active' %}:</td>
<td>
<span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" {% if object.is_active %} checked {% endif %} {% if request.user == object %} disabled {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Force enabled MFA' %}:</td>
<td>
<span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" class="onoffswitch-checkbox" {% if object.mfa_force_enabled %} checked {% endif %}
id="force_enable_mfa">
<label class="onoffswitch-label" for="force_enable_mfa">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Reset MFA' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn-reset-mfa" style="width: 54px">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
{% if object.can_update_password %}
<tr>
<td>{% trans 'Send reset password mail' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" {% if request.user == object %} disabled="disabled" {% endif %} id="btn-reset-password" style="width: 54px">{% trans 'Send' %}</button>
</span>
</td>
</tr>
{% endif %}
{% if object.can_update_ssh_key %}
<tr>
<td>{% trans 'Send reset ssh key mail' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" {% if request.user == object %} disabled="disabled" {% endif %} id="btn-reset-pk" style="width: 54px;">{% trans 'Send' %}</button>
</span>
</td>
</tr>
{% endif %}
<tr style="{% if not unblock %}display:none{% endif %}">
<td>{% trans 'Unblock user' %}</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn-unblock-user" style="width: 54px">{% trans 'Unblock' %}</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
{% if request.user.can_admin_current_org %}
{% if object.can_user_current_org or object.can_admin_current_org %}
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Join user groups' %}" id="groups_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}" >{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_join_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in object.groups.all %}
<tr>
<td >
<b class="bdg_group" >{{ group.name }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" data-uid={{ group.id }} type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if LICENSE_VALID and LOGIN_CONFIRM_ENABLE %}
<div class="panel panel-warning">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Login confirm' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Reviewers' %}" id="id_assignees" class="users-select2" style="width: 100%" multiple="" tabindex="4">
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-warning btn-small" id="btn_reviewer_confirm">{% trans 'Confirm' %}</button>
</td>
</tr>
</form>
{% if object.get_login_confirm_setting %}
{% for u in object.login_confirm_setting.reviewers.all %}
<tr>
<td >
<b class="bdg_reviewer">{{ u }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn-leave-reviewer" data-uid={{ u.id }} type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% endif %}
</div>
{% include 'users/_user_update_pk_modal.html' %}
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var usersSelect2; var usersSelect2;
function updateUserLoginReviewer(reviewers) { function updateUserLoginReviewer(reviewers) {
var url = "{% url 'api-auth:login-confirm-setting-update' user_id=user_object.id %}"; var url = "{% url 'api-auth:login-confirm-setting-update' user_id=object.id %}";
var data = {reviewers: reviewers}; var data = {reviewers: reviewers};
requestApi({ requestApi({
url: url, url: url,
@ -363,7 +348,7 @@ $(document).ready(function() {
usersSelect2 = usersSelect2Init('.users-select2') usersSelect2 = usersSelect2Init('.users-select2')
}) })
.on('click', '#is_active', function() { .on('click', '#is_active', function() {
var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}"; var the_url = "{% url 'api-users:user-detail' pk=object.id %}";
var checked = $(this).prop('checked'); var checked = $(this).prop('checked');
var body = { var body = {
'is_active': checked 'is_active': checked
@ -376,12 +361,12 @@ $(document).ready(function() {
}); });
}) })
.on('click', '#force_enable_mfa', function() { .on('click', '#force_enable_mfa', function() {
{% if request.user == user_object %} {% if request.user == object %}
toastr.error("{% trans 'Goto profile page enable MFA' %}"); toastr.error("{% trans 'Goto profile page enable MFA' %}");
return; return;
{% endif %} {% endif %}
var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}"; var the_url = "{% url 'api-users:user-detail' pk=object.id %}";
var checked = $(this).prop('checked'); var checked = $(this).prop('checked');
var mfa_level; var mfa_level;
var otp_secret_key; var otp_secret_key;
@ -410,7 +395,7 @@ $(document).ready(function() {
removeObject(objectId) removeObject(objectId)
}).on('click', '#btn-reset-password', function() { }).on('click', '#btn-reset-password', function() {
function doReset() { function doReset() {
var the_url = '{% url "api-users:user-reset-password" pk=user_object.id %}'; var the_url = '{% url "api-users:user-reset-password" pk=object.id %}';
var body = {}; var body = {};
var success = function() { var success = function() {
var msg = "{% trans "An e-mail has been sent to the user`s mailbox." %}"; var msg = "{% trans "An e-mail has been sent to the user`s mailbox." %}";
@ -436,7 +421,7 @@ $(document).ready(function() {
}); });
}).on('click', '#btn-reset-pk', function() { }).on('click', '#btn-reset-pk', function() {
function doReset() { function doReset() {
var the_url = '{% url "api-users:user-public-key-reset" pk=user_object.id %}'; var the_url = '{% url "api-users:user-public-key-reset" pk=object.id %}';
var body = {}; var body = {};
var success = function() { var success = function() {
var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}"; var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}";
@ -488,15 +473,15 @@ $(document).ready(function() {
requestApi({ url: the_url, body: JSON.stringify(body), success: success, error: fail}); requestApi({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
}).on('click', '.btn-delete-user', function () { }).on('click', '.btn-delete-user', function () {
var $this = $(this); var $this = $(this);
var name = "{{ user_object.name }}"; var name = "{{ object.name }}";
var uid = "{{ user_object.id }}"; var uid = "{{ object.id }}";
var the_url = '{% url "api-users:user-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid); var the_url = '{% url "api-users:user-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
var redirect_url = "{% url 'users:user-list' %}"; var redirect_url = "{% url 'users:user-list' %}";
objectDelete($this, name, the_url, redirect_url); objectDelete($this, name, the_url, redirect_url);
}).on('click', '#btn-unblock-user', function () { }).on('click', '#btn-unblock-user', function () {
function doReset() { function doReset() {
{#var the_url = '{% url "api-users:user-reset-password" pk=user_object.id %}';#} {#var the_url = '{% url "api-users:user-reset-password" pk=object.id %}';#}
var the_url = '{% url "api-users:user-unblock" pk=user_object.id %}'; var the_url = '{% url "api-users:user-unblock" pk=object.id %}';
var body = {}; var body = {};
var success = function() { var success = function() {
var msg = "{% trans "Success" %}"; var msg = "{% trans "Success" %}";
@ -530,7 +515,7 @@ $(document).ready(function() {
}); });
}).on('click', '#btn-reset-mfa', function () { }).on('click', '#btn-reset-mfa', function () {
requestApi({ requestApi({
url: "{% url 'api-users:user-reset-otp' pk=user_object.id %}", url: "{% url 'api-users:user-reset-otp' pk=object.id %}",
method: "GET", method: "GET",
success_message: "{% trans 'Reset user MFA success' %}" success_message: "{% trans 'Reset user MFA success' %}"
}) })

View File

@ -1,4 +1,4 @@
{% extends 'base.html' %} {% extends 'users/_base_user_detail.html' %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
@ -7,32 +7,11 @@
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> {% block content_table %}
<div class="row"> {% include 'users/_granted_assets.html' %}
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'users:user-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
</li>
<li class="active">
<a href="{% url 'users:user-granted-asset' pk=object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
</li>
<li>
<a href="{% url 'users:user-asset-permission' pk=object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset permission' %}</a>
</li>
</ul>
</div>
<div class="tab-content">
{% include 'users/_granted_assets.html' %}
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var assetTableUrl = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1"; var assetTableUrl = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1";

View File

@ -0,0 +1,100 @@
{% extends 'users/_base_user_detail.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
{% endblock %}
{% block content_table %}
<div class="col-sm-10" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-striped table-bordered table-hover " id="database_app_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'Host' %}</th>
<th class="text-center">{% trans 'Database' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var inited = false;
var database_app_table, url;
function initTable() {
if (inited){
return
} else {
inited = true;
}
url = '{% url "api-perms:user-database-apps" pk=object.id %}';
var options = {
ele: $('#database_app_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
cellData = htmlEscape(cellData);
{% url 'applications:database-app-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData, rowData) {
var type = htmlEscape(rowData.get_type_display);
$(td).html(type);
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var host = htmlEscape(cellData);
$(td).html(host);
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var database = htmlEscape(cellData);
$(td).html(database);
}}
],
ajax_url: url,
columns: [
{data: "id"},
{data: "name"},
{data: "type"},
{data: "host"},
{data: "database"},
{data: "comment", orderable: false},
]
};
database_app_table = jumpserver.initServerSideDataTable(options);
return database_app_table
}
$(document).ready(function(){
initTable();
})
</script>
{% endblock %}

View File

@ -0,0 +1,93 @@
{% extends 'users/_base_user_detail.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
{% endblock %}
{% block content_table %}
<div class="col-sm-10" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-striped table-bordered table-hover " id="remote_app_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'App type' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var inited = false;
var remote_app_table, url;
function initTable() {
if (inited){
return
} else {
inited = true;
}
url = '{% url "api-perms:user-remote-apps" pk=object.id %}';
var options = {
ele: $('#remote_app_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
cellData = htmlEscape(cellData);
{% url 'applications:remote-app-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 1, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.get_type_display)
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var hostname = htmlEscape(cellData.hostname);
$(td).html(hostname);
}}
],
ajax_url: url,
columns: [
{data: "id"},
{data: "name"},
{data: "type"},
{data: "asset_info", orderable: false},
{data: "comment", orderable: false}
]
};
remote_app_table = jumpserver.initServerSideDataTable(options);
return remote_app_table
}
$(document).ready(function(){
initTable();
})
</script>
{% endblock %}

View File

@ -62,7 +62,7 @@ function initTable() {
], ],
ajax_url: '{% url "api-users:user-group-list" %}', ajax_url: '{% url "api-users:user-group-list" %}',
columns: [{data: "id"}, {data: "name" }, {data: "users_amount", orderable: false}, columns: [{data: "id"}, {data: "name" }, {data: "users_amount", orderable: false},
{data: "comment"}, {data: "id", orderable: false, width:"100px"}], {data: "comment"}, {data: "id", orderable: false, width: "120px"}],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
groupsTable = jumpserver.initServerSideDataTable(options); groupsTable = jumpserver.initServerSideDataTable(options);

View File

@ -121,7 +121,7 @@ function initTable() {
{data: "groups_display", orderable: false}, {data: "groups_display", orderable: false},
{data: "source"}, {data: "source"},
{data: "is_valid", orderable: false, width: "50px"}, {data: "is_valid", orderable: false, width: "50px"},
{data: "id", orderable: false, width: "100px"} {data: "id", orderable: false, width: "120px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };

View File

@ -0,0 +1,168 @@
{% extends 'users/_base_user_detail.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content_table %}
<div class="col-sm-10" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-striped table-bordered table-hover"
id="permission_list_table"
style="width: 100%">
<thead>
<tr>
<th></th>
<th>{% trans 'Name' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'RemoteApp' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Validity' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
function format(d) {
var data = "";
if (d.users.length > 0 ) {
data += makeLabel(["{% trans 'User' %}", d.users.join(", ")])
}
if (d.user_groups.length > 0) {
data += makeLabel(["{% trans 'User group' %}", d.user_groups.join(", ")])
}
if (d.remote_apps.length > 0) {
data += makeLabel(["{% trans 'RemoteApp' %}", d.remote_apps.join(", ")])
}
if (d.system_users.length > 0) {
data += makeLabel(["{% trans 'System user' %}", d.system_users.join(", ")])
}
return data
}
function initTable() {
var options = {
ele: $('#permission_list_table'),
toggle: true,
columnDefs: [
{targets: 0, createdCell: function (td, cellData, rowData) {
$(td).addClass("toggle");
$(td).html("<i class='fa fa-angle-right'></i>");
}},
{targets: 1, createdCell: function (td, cellData, rowData) {
cellData = htmlEscape(cellData);
var detail_btn = '<a href="{% url "perms:remote-app-permission-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 3, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 4, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 5, createdCell: function (td, cellData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 6, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(rowData.name);
var update_btn = '<a href="{% url "perms:remote-app-permission-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-uid="{{ DEFAULT_PK }}" mark=1 data-name="99991938">{% trans "Delete" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', name);
$(td).html(update_btn + del_btn);
}}
],
ajax_url: '{% url "api-perms:remote-app-permission-list" %}?user_id={{ object.id }}',
columns: [
{data: "id"}, {data: "name"}, {data: "users", orderable: false},
{data: "user_groups", orderable: false}, {data: "remote_apps", orderable: false},
{data: "system_users", orderable: false}, {data: "is_valid", orderable: false},
{data: "id", orderable: false, width: "120px"}
],
select: {},
op_html: $('#actions').html()
};
table = jumpserver.initServerSideDataTable(options);
return table
}
$(document).ready(function() {
initTable();
})
.on('click', '.toggle', function (e) {
e.preventDefault();
var detailRows = [];
var tr = $(this).closest('tr');
var row = table.row(tr);
var idx = $.inArray(tr.attr('id'), detailRows);
if (row.child.isShown()) {
tr.removeClass('details');
$(this).children('i:first-child').removeClass('fa-angle-down').addClass('fa-angle-right');
row.child.hide();
// Remove from the 'open' array
detailRows.splice(idx, 1);
}
else {
tr.addClass('details');
$(this).children('i:first-child').removeClass('fa-angle-right').addClass('fa-angle-down');
row.child(format(row.data())).show();
// Add to the 'open' array
if ( idx === -1 ) {
detailRows.push(tr.attr('id'));
}
}
})
.on('click', '.btn-del', function () {
var $this = $(this);
var uid = $this.data('uid');
var name = $this.data('name');
var the_url = '{% url "api-perms:remote-app-permission-detail" pk=DEFAULT_PK %}'
.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
})
</script>
{% endblock %}

View File

@ -36,6 +36,10 @@ urlpatterns = [
path('user/<uuid:pk>/', views.UserDetailView.as_view(), name='user-detail'), path('user/<uuid:pk>/', views.UserDetailView.as_view(), name='user-detail'),
path('user/<uuid:pk>/assets/', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), path('user/<uuid:pk>/assets/', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
path('user/<uuid:pk>/asset-permissions/', views.UserAssetPermissionListView.as_view(), name='user-asset-permission'), path('user/<uuid:pk>/asset-permissions/', views.UserAssetPermissionListView.as_view(), name='user-asset-permission'),
path('user/<uuid:pk>/remote-apps/', views.UserGrantedRemoteAppView.as_view(), name='user-granted-remote-app'),
path('user/<uuid:pk>/remote-app-permissions/', views.UserRemoteAppPermissionListView.as_view(), name='user-remote-app-permission'),
path('user/<uuid:pk>/database-apps/', views.UserGrantedDatabasesAppView.as_view(), name='user-granted-database-app'),
path('user/<uuid:pk>/database-app-permissions/', views.UserDatabaseAppPermissionListView.as_view(), name='user-database-app-permission'),
path('user/<uuid:pk>/login-history/', views.UserDetailView.as_view(), name='user-login-history'), path('user/<uuid:pk>/login-history/', views.UserDetailView.as_view(), name='user-login-history'),
# User group view # User group view

View File

@ -31,8 +31,10 @@ from ..signals import post_user_create
__all__ = [ __all__ = [
'UserListView', 'UserCreateView', 'UserDetailView', 'UserListView', 'UserCreateView', 'UserDetailView',
'UserUpdateView', 'UserGrantedAssetView', 'UserUpdateView', 'UserBulkUpdateView',
'UserBulkUpdateView', 'UserAssetPermissionListView', 'UserGrantedAssetView', 'UserAssetPermissionListView',
'UserGrantedRemoteAppView', 'UserRemoteAppPermissionListView',
'UserGrantedDatabasesAppView', 'UserDatabaseAppPermissionListView',
] ]
logger = get_logger(__name__) logger = get_logger(__name__)
@ -164,7 +166,7 @@ class UserBulkUpdateView(PermissionsMixin, TemplateView):
class UserDetailView(PermissionsMixin, DetailView): class UserDetailView(PermissionsMixin, DetailView):
model = User model = User
template_name = 'users/user_detail.html' template_name = 'users/user_detail.html'
context_object_name = "user_object" context_object_name = "object"
key_prefix_block = "_LOGIN_BLOCK_{}" key_prefix_block = "_LOGIN_BLOCK_{}"
permission_classes = [IsOrgAdmin] permission_classes = [IsOrgAdmin]
@ -220,3 +222,59 @@ class UserAssetPermissionListView(PermissionsMixin, DetailView):
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class UserGrantedRemoteAppView(PermissionsMixin, DetailView):
model = User
template_name = 'users/user_granted_remote_app.html'
permission_classes = [IsOrgAdmin]
def get_context_data(self, **kwargs):
context = {
'app': _('Users'),
'action': _('User granted RemoteApp'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class UserRemoteAppPermissionListView(PermissionsMixin, DetailView):
model = User
template_name = 'users/user_remote_app_permission.html'
permission_classes = [IsOrgAdmin]
def get_context_data(self, **kwargs):
context = {
'app': _('Users'),
'action': _('RemoteApp permission'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class UserGrantedDatabasesAppView(PermissionsMixin, DetailView):
model = User
template_name = 'users/user_granted_database_app.html'
permission_classes = [IsOrgAdmin]
def get_context_data(self, **kwargs):
context = {
'app': _('Users'),
'action': _('User granted DatabaseApp'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class UserDatabaseAppPermissionListView(PermissionsMixin, DetailView):
model = User
template_name = 'users/user_database_app_permission.html'
permission_classes = [IsOrgAdmin]
def get_context_data(self, **kwargs):
context = {
'app': _('Users'),
'action': _('DatabaseApp permission'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)