feat: 工作台区分组织 (#8040)

* perf: 工作台受组织角色控制

* perf: workspace => workbench

* perf: 修改 workspace codename

Co-authored-by: ibuler <ibuler@qq.com>
pull/8047/head
fit2bot 2022-04-12 14:25:49 +08:00 committed by GitHub
parent 7c7d7d52b2
commit 1f8ded49fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 77 additions and 1857 deletions

View File

@ -55,7 +55,6 @@ urlpatterns = [
path('profile/otp/enable/bind/', users_view.UserOtpEnableBindView.as_view(), name='user-otp-enable-bind'), path('profile/otp/enable/bind/', users_view.UserOtpEnableBindView.as_view(), name='user-otp-enable-bind'),
path('profile/otp/disable/', users_view.UserOtpDisableView.as_view(), path('profile/otp/disable/', users_view.UserOtpDisableView.as_view(),
name='user-otp-disable'), name='user-otp-disable'),
path('first-login/', users_view.UserFirstLoginView.as_view(), name='user-first-login'),
# openid # openid
path('cas/', include(('authentication.backends.cas.urls', 'authentication'), namespace='cas')), path('cas/', include(('authentication.backends.cas.urls', 'authentication'), namespace='cas')),

View File

@ -1,4 +1,4 @@
from django.views.generic import TemplateView from django.views.generic import View
from django.shortcuts import redirect from django.shortcuts import redirect
from common.permissions import IsValidUser from common.permissions import IsValidUser
from common.mixins.views import PermissionsMixin from common.mixins.views import PermissionsMixin
@ -6,8 +6,7 @@ from common.mixins.views import PermissionsMixin
__all__ = ['IndexView'] __all__ = ['IndexView']
class IndexView(PermissionsMixin, TemplateView): class IndexView(PermissionsMixin, View):
template_name = 'index.html'
permission_classes = [IsValidUser] permission_classes = [IsValidUser]
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:17e378f009274c169039e815158ea9072ee89811bb27a5b17a628f2066fcfb86 oid sha256:097c6d06ed8dcf2e1807560b6eb52d98cba31f25fe8d67ce4315668c150ca6b8
size 129989 size 129989

View File

@ -3188,7 +3188,7 @@ msgid "Can view audit view"
msgstr "監査ビューを表示できます" msgstr "監査ビューを表示できます"
#: rbac/models/menu.py:17 #: rbac/models/menu.py:17
msgid "Can view workspace view" msgid "Can view workbench view"
msgstr "ワークスペースビューを表示できます" msgstr "ワークスペースビューを表示できます"
#: rbac/models/menu.py:18 #: rbac/models/menu.py:18
@ -3271,7 +3271,7 @@ msgid "Console view"
msgstr "コンソールビュー" msgstr "コンソールビュー"
#: rbac/tree.py:27 #: rbac/tree.py:27
msgid "Workspace view" msgid "Workbench view"
msgstr "ワークスペースビュー" msgstr "ワークスペースビュー"
#: rbac/tree.py:28 #: rbac/tree.py:28

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:51c19db490e2e3a7cc3c3fce33b2e4422239d8c64d591208cadaf062c5ccb0c9 oid sha256:3d6a0d40534209f3ffab0b0ecfab7ec82f137156fc8e7b19ce1711036d14aeca
size 107709 size 107709

View File

@ -3151,7 +3151,7 @@ msgid "Can view audit view"
msgstr "可以显示审计台" msgstr "可以显示审计台"
#: rbac/models/menu.py:17 #: rbac/models/menu.py:17
msgid "Can view workspace view" msgid "Can view workbench view"
msgstr "可以显示工作台" msgstr "可以显示工作台"
#: rbac/models/menu.py:18 #: rbac/models/menu.py:18
@ -3233,7 +3233,7 @@ msgid "Console view"
msgstr "控制台" msgstr "控制台"
#: rbac/tree.py:27 #: rbac/tree.py:27
msgid "Workspace view" msgid "Workbench view"
msgstr "工作台" msgstr "工作台"
#: rbac/tree.py:28 #: rbac/tree.py:28

View File

@ -22,7 +22,3 @@ class AppRoleUserMixin(_RoleUserMixin):
('get_tree', 'perms.view_myapps'), ('get_tree', 'perms.view_myapps'),
('GET', 'perms.view_myapps'), ('GET', 'perms.view_myapps'),
) )
def dispatch(self, *args, **kwargs):
with tmp_to_root_org():
return super().dispatch(*args, **kwargs)

View File

@ -36,7 +36,3 @@ class AssetRoleUserMixin(PermBaseMixin, _RoleUserMixin):
('get_tree', 'perms.view_myassets'), ('get_tree', 'perms.view_myassets'),
('GET', 'perms.view_myassets'), ('GET', 'perms.view_myassets'),
) )
def dispatch(self, *args, **kwargs):
with tmp_to_root_org():
return super().dispatch(*args, **kwargs)

View File

@ -202,7 +202,9 @@ class UserGrantedTreeRefreshController:
user = self.user user = self.user
with tmp_to_root_org(): with tmp_to_root_org():
UserAssetGrantedTreeNodeRelation.objects.filter(user=user).exclude(org_id__in=self.org_ids).delete() UserAssetGrantedTreeNodeRelation.objects.filter(user=user)\
.exclude(org_id__in=self.org_ids)\
.delete()
if force or self.have_need_refresh_orgs(): if force or self.have_need_refresh_orgs():
with UserGrantedTreeRebuildLock(user_id=user.id): with UserGrantedTreeRebuildLock(user_id=user.id):
@ -219,7 +221,9 @@ class UserGrantedTreeRefreshController:
utils = UserGrantedTreeBuildUtils(user) utils = UserGrantedTreeBuildUtils(user)
utils.rebuild_user_granted_tree() utils.rebuild_user_granted_tree()
logger.info( logger.info(
f'Rebuild user tree ok: cost={time.time() - t_start} user={self.user} org={current_org}') f'Rebuild user tree ok: cost={time.time() - t_start} '
f'user={self.user} org={current_org}'
)
class UserGrantedUtilsBase: class UserGrantedUtilsBase:

View File

@ -5,7 +5,7 @@ from .const import Scope, system_exclude_permissions, org_exclude_permissions
# Todo: 获取应该区分 系统用户,和组织用户的权限 # Todo: 获取应该区分 系统用户,和组织用户的权限
# 工作台也区分组织后再考虑 # 工作台也区分组织后再考虑
user_perms = ( user_perms = (
('rbac', 'menupermission', 'view', 'workspace'), ('rbac', 'menupermission', 'view', 'workbench'),
('rbac', 'menupermission', 'view', 'webterminal'), ('rbac', 'menupermission', 'view', 'webterminal'),
('rbac', 'menupermission', 'view', 'filemanager'), ('rbac', 'menupermission', 'view', 'filemanager'),
('perms', 'permedasset', 'view,connect', 'myassets'), ('perms', 'permedasset', 'view,connect', 'myassets'),
@ -17,6 +17,7 @@ user_perms = (
('ops', 'commandexecution', 'add', 'commandexecution'), ('ops', 'commandexecution', 'add', 'commandexecution'),
('authentication', 'connectiontoken', 'add', 'connectiontoken'), ('authentication', 'connectiontoken', 'add', 'connectiontoken'),
('tickets', 'ticket', 'view', 'ticket'), ('tickets', 'ticket', 'view', 'ticket'),
('orgs', 'organization', 'view', 'rootorg'),
) )
auditor_perms = user_perms + ( auditor_perms = user_perms + (

View File

@ -27,7 +27,7 @@ class Migration(migrations.Migration):
], ],
options={ options={
'verbose_name': 'Menu permission', 'verbose_name': 'Menu permission',
'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view')], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workbench view')],
'default_permissions': [], 'default_permissions': [],
}, },
), ),

View File

@ -12,6 +12,6 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='menupermission', name='menupermission',
options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager')], 'verbose_name': 'Menu permission'}, options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workbench view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager')], 'verbose_name': 'Menu permission'},
), ),
] ]

View File

@ -12,6 +12,6 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='menupermission', name='menupermission',
options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager') ], 'verbose_name': 'Menu permission'}, options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workbench view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager') ], 'verbose_name': 'Menu permission'},
), ),
] ]

View File

@ -0,0 +1,22 @@
# Generated by Django 3.1.14 on 2022-04-11 09:09
from django.db import migrations
def migrate_workspace_to_workbench(apps, *args):
model = apps.get_model('auth', 'Permission')
model.objects.filter(codename='view_workspace').delete()
class Migration(migrations.Migration):
dependencies = [
('rbac', '0007_auto_20220314_1525'),
]
operations = [
migrations.AlterModelOptions(
name='menupermission',
options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workbench', 'Can view workbench view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager')], 'verbose_name': 'Menu permission'},
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.1.14 on 2022-04-11 09:24
from django.db import migrations
def migrate_workspace_to_workbench(apps, *args):
model = apps.get_model('auth', 'Permission')
model.objects.filter(codename='view_workspace').delete()
class Migration(migrations.Migration):
dependencies = [
('rbac', '0008_auto_20220411_1709'),
]
operations = [
migrations.RunPython(migrate_workspace_to_workbench)
]

View File

@ -14,7 +14,7 @@ class MenuPermission(models.Model):
permissions = [ permissions = [
('view_console', _('Can view console view')), ('view_console', _('Can view console view')),
('view_audit', _('Can view audit view')), ('view_audit', _('Can view audit view')),
('view_workspace', _('Can view workspace view')), ('view_workbench', _('Can view workbench view')),
('view_webterminal', _('Can view web terminal')), ('view_webterminal', _('Can view web terminal')),
('view_filemanager', _('Can view file manager')), ('view_filemanager', _('Can view file manager')),
] ]

View File

@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
import os
from collections import defaultdict from collections import defaultdict
from typing import Callable from typing import Callable
import os
from django.utils.translation import gettext_lazy as _, gettext, get_language from django.utils.translation import gettext_lazy as _, gettext, get_language
from django.conf import settings from django.conf import settings
@ -24,7 +24,7 @@ root_node_data = {
# 第二层 view 节点,手动创建的 # 第二层 view 节点,手动创建的
view_nodes_data = [ view_nodes_data = [
{'id': 'view_console', 'name': _('Console view')}, {'id': 'view_console', 'name': _('Console view')},
{'id': 'view_workspace', 'name': _('Workspace view')}, {'id': 'view_workbench', 'name': _('Workbench view')},
{'id': 'view_audit', 'name': _('Audit view')}, {'id': 'view_audit', 'name': _('Audit view')},
{'id': 'view_setting', 'name': _('System setting')}, {'id': 'view_setting', 'name': _('System setting')},
{'id': 'view_other', 'name': _('Other')}, {'id': 'view_other', 'name': _('Other')},
@ -55,8 +55,8 @@ extra_nodes_data = [
{"id": "app_change_plan_node", "name": _("App change auth"), "pId": "accounts"}, {"id": "app_change_plan_node", "name": _("App change auth"), "pId": "accounts"},
{"id": "asset_change_plan_node", "name": _("Asset change auth"), "pId": "accounts"}, {"id": "asset_change_plan_node", "name": _("Asset change auth"), "pId": "accounts"},
{"id": "terminal_node", "name": _("Terminal setting"), "pId": "view_setting"}, {"id": "terminal_node", "name": _("Terminal setting"), "pId": "view_setting"},
{'id': "my_assets", "name": _("My assets"), "pId": "view_workspace"}, {'id': "my_assets", "name": _("My assets"), "pId": "view_workbench"},
{'id': "my_apps", "name": _("My apps"), "pId": "view_workspace"}, {'id': "my_apps", "name": _("My apps"), "pId": "view_workbench"},
] ]
# 将 model 放到其它节点下,而不是本来的 app 中 # 将 model 放到其它节点下,而不是本来的 app 中
@ -89,7 +89,7 @@ special_pid_mapper = {
'audits.ftplog': 'terminal', 'audits.ftplog': 'terminal',
'perms.view_myassets': 'my_assets', 'perms.view_myassets': 'my_assets',
'perms.view_myapps': 'my_apps', 'perms.view_myapps': 'my_apps',
'ops.add_commandexecution': 'view_workspace', 'ops.add_commandexecution': 'view_workbench',
'ops.view_commandexecution': 'audits', 'ops.view_commandexecution': 'audits',
"perms.view_mykubernetsapp": "my_apps", "perms.view_mykubernetsapp": "my_apps",
"perms.connect_mykubernetsapp": "my_apps", "perms.connect_mykubernetsapp": "my_apps",
@ -102,9 +102,9 @@ special_pid_mapper = {
"settings.view_setting": "view_setting", "settings.view_setting": "view_setting",
"rbac.view_console": "view_console", "rbac.view_console": "view_console",
"rbac.view_audit": "view_audit", "rbac.view_audit": "view_audit",
"rbac.view_workspace": "view_workspace", "rbac.view_workbench": "view_workbench",
"rbac.view_webterminal": "view_workspace", "rbac.view_webterminal": "view_workbench",
"rbac.view_filemanager": "view_workspace", "rbac.view_filemanager": "view_workbench",
'tickets.view_ticket': 'tickets' 'tickets.view_ticket': 'tickets'
} }

View File

@ -1,54 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block help_message %}
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-sm-3" id="split-left" style="padding-left: 3px;padding-right: 0">
{% include 'assets/_node_tree.html' %}
</div>
<div class="col-sm-9 animated fadeInRight" id="split-right">
<div class="tree-toggle" style="z-index: 10">
<div class="btn btn-sm btn-primary tree-toggle-btn" onclick="toggleSpliter()">
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
</div>
</div>
<div class="mail-box-header">
{% block table_container %}
<table class="table table-striped table-bordered table-hover" id="{% block table_id %}editable{% endblock %}" >
<thead>
<tr>
{% block table_head %} {% endblock %}
</tr>
</thead>
<tbody>
{% block table_body %} {% endblock %}
</tbody>
</table>
{% endblock %}
</div>
</div>
</div>
</div>
<script>
var showTree = 1;
function toggleSpliter() {
if (showTree === 1) {
$("#split-left").hide(500, function () {
$("#split-right").attr("class", "col-sm-12");
$("#toggle-icon").attr("class", "fa fa-angle-right fa-x");
showTree = 1;
});
} else {
$("#split-right").attr("class", "col-sm-9");
$("#toggle-icon").attr("class", "fa fa-angle-left fa-x");
$("#split-left").show(500);
showTree = 0;
}
}
</script>
{% endblock %}

View File

@ -1,43 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap3 %}
{% block custom_head_css_js %}
<script type="text/javascript" src="{% static 'js/pwstrength-bootstrap.js' %}"></script>
{% block custom_head_css_js_create %} {% 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="ibox-title">
<h5>{{ action }}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
{% if form.errors.all %}
<div class="alert alert-danger" style="margin: 20px auto 0px">
{{ form.errors.all }}
</div>
{% endif %}
{% block form %}
{% endblock %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,70 +0,0 @@
{% 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="ibox-title">
<h5>
{{ action }}
</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="" id="content_start">
{% block content_left_head %} {% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right mail-search form-inline">
{% block search_form %}
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
{% trans 'Search' %}
</button>
</div>
</div>
{% endblock %}
</form>
{% endblock %}
</div>
{% block table_container %}
<table class="table table-striped table-bordered table-hover" id="editable" >
<thead>
<tr>
{% block table_head %} {% endblock %}
</tr>
</thead>
<tbody>
{% block table_body %} {% endblock %}
</tbody>
</table>
{% endblock %}
<div class="row">
<div class="col-sm-4">
{% block content_bottom_left %} {% endblock %}
</div>
{% block table_pagination %}
{% include '_pagination.html' %}
{% endblock %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,66 +0,0 @@
{% load i18n %}
{% load common_tags %}
{% if is_paginated %}
<div class="col-sm-4">
<div class="dataTables_info text-center" id="editable_info" role="status" aria-live="polite">
{# 显示第 {{ page_obj.start_index }} 至 {{ page_obj.end_index }} 项结果,共 {{ paginator.count }} 项#}
</div>
</div>
<div class="col-sm-4">
<div class="dataTables_paginate paging_simple_numbers" id="editable_paginate">
<ul class="pagination" style="margin-top: 0; float: right">
{% if page_obj.has_previous %}
<li class="paginate_button previous" aria-controls="editable" tabindex="0" id="previous">
<a data-page="next" class="page" href="?page={{ page_obj.previous_page_number}}"></a>
</li>
{% endif %}
{% for page in paginator.num_pages|pagination_range:page_obj.number %}
{% if page == page_obj.number %}
<li class="paginate_button active" aria-controls="editable" tabindex="0">
{% else %}
<li class="paginate_button" aria-controls="editable" tabindex="0">
{% endif %}
<a class="page" href="?page={{ page }}" title="{{ page }}页">{{ page }}</a>
</li>
{% endfor %}
{% if page_obj.has_next %}
<li class="paginate_button next" aria-controls="editable" tabindex="0" id="next">
<a data-page="next" class="page" href="?page={{ page_obj.next_page_number }}"></a>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
<script>
$(document).ready(function () {
$('.page').click(function () {
var searchStr = location.search;
var old_href = $(this).attr('href').replace('?', '');
var searchArray = searchStr.split('&');
if (searchStr === '') {
searchStr = '?page=1'
}
if (searchStr.indexOf('page') >= 0) {
searchArray.pop();
}
searchArray.push(old_href);
if (searchArray.length > 1) {
$(this).attr('href', searchArray.join('&'));
}
})
$('#editable_info').html(
"{% trans 'Displays the results of items _START_ to _END_; A total of _TOTAL_ entries' %}"
.replace('_START_', {{ page_obj.start_index }})
.replace('_END_', {{ page_obj.end_index }})
.replace('_TOTAL_', {{ paginator.count }})
)
});
</script>

View File

@ -1,15 +0,0 @@
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% trans 'Confirm delete' %}</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>{% trans 'Are you sure delete' %} <b>{{ object.name }} </b> ?</p>
<input type="submit" value="Confirm" />
</form>
</body>
</html>

View File

@ -1,618 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% block content %}
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-sm-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-success pull-right">Users</span>
<h5>{% trans 'Total users' %}</h5>
</div>
<div class="ibox-content">
<h1 class="no-margins"><a href="{% url 'users:user-list' %}"><span id="total_count_users"></span></a></h1>
<small>All users</small>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-info pull-right">Assets</span>
<h5>{% trans 'Total assets' %}</h5>
</div>
<div class="ibox-content">
<h1 class="no-margins"><a href="{% url 'assets:asset-list' %}"><span id="total_count_assets"></span></a></h1>
<small>All assets</small>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary pull-right">Online</span>
<h5>{% trans 'Online users' %}</h5>
</div>
<div class="ibox-content">
<h1 class="no-margins"><a href="{% url 'terminal:session-online-list' %}"> <span id="total_count_online_users"></span></a></h1>
<small>Online users</small>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-danger pull-right">Connected</span>
<h5>{% trans 'Online sessions' %}</h5>
</div>
<div class="ibox-content">
<h1 class="no-margins"><a href="{% url 'terminal:session-online-list' %}"> <span id="total_count_online_sessions"></span></a></h1>
<small>Online sessions</small>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2 border-bottom white-bg dashboard-header" style="margin-left:15px;height: 346px">
<small>{% trans 'In the past week, a total of ' %}<span class="text-info" id="dates_total_count_login_users"></span>{% trans ' users have logged in ' %}<span class="text-success" id="dates_total_count_login_times"></span>{% trans ' times asset.' %}</small>
<ul class="list-group clear-list m-t" id="dates_login_times_top5_users">
</ul>
</div>
<div class="col-sm-7" id="dates_metrics_echarts" style="margin-left: -15px;height: 346px;padding: 15px 0 15px 0;"></div>
<div class="col-sm-3 white-bg" id="top1" style="margin-left: -15px;height: 346px">
<div class="statistic-box">
<h4>
{% trans 'Active user asset ratio' %}
</h4>
<p>
{% trans 'The following graphs describe the percentage of active users per month and assets per user host per month, respectively.' %}
</p>
<div class="row text-center">
<div class="col-sm-6">
<div id="dates_total_count_users_pie" style="width: 140px; height: 140px;">
</div>
<h5>{% trans 'User' %}</h5>
</div>
<div class="col-sm-6">
<div id="dates_total_count_assets_pie" style="width: 140px; height: 140px;"></div>
<h5>{% trans 'Asset' %}</h5>
</div>
</div>
<div class="m-t">
<small></small>
</div>
</div>
</div>
</div>
<br/>
<div class="row">
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Top 10 assets in a week' %}</h5>
<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 ibox-heading">
<h3><i class="fa fa-inbox"></i>{% trans 'Top 10 assets in a week'%}</h3>
<small><i class="fa fa-map-marker"></i>{% trans 'Login frequency and last login record.' %}</small>
</div>
<div class="ibox-content inspinia-timeline" id="dates_login_times_top10_assets">
</div>
</div>
</div>
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Last 10 login' %}</h5>
<div class="ibox-tools">
<span class="label label-info-light">10 Messages</span>
</div>
</div>
<div class="ibox-content ibox-heading">
<h3><i class="fa fa-paper-plane-o"></i> {% trans 'Login record' %}</h3>
<small><i class="fa fa-map-marker"></i>{% trans 'Last 10 login records.' %}</small>
</div>
<div class="ibox-content">
<div>
<div class="feed-activity-list" id="dates_login_record_top10_sessions">
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Top 10 users in a week' %}</h5>
<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 ibox-heading">
<h3><i class="fa fa-user"></i>{% trans 'Top 10 users in a week' %}</h3>
<small><i class="fa fa-map-marker"></i>{% trans 'User login frequency and last login record.' %}</small>
</div>
<div class="ibox-content inspinia-timeline" id="dates_login_times_top10_users">
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/echarts/echarts.js' %}"></script>
<script>
function requireMonthMetricsECharts(data){
require(
[
'echarts',
'echarts/chart/line'
],
function (ec) {
var monthMetricsECharts = ec.init(document.getElementById('dates_metrics_echarts'));
var option = {
title : {
text: "{% trans 'Monthly data overview' %}",
subtext: "{% trans 'History summary in one month' %}",
x: 'center'
},
tooltip : {
trigger: 'axis'
},
backgroundColor: '#fff',
legend: {
data:["{% trans 'Login count' %}", "{% trans 'Active users' %}", "{% trans 'Active assets' %}"],
y: 'bottom'
},
toolbox: {
show : false,
feature : {
magicType : {show: true, type: ['line', 'bar']}
}
},
calculable : true,
xAxis : [
{
type : 'category',
boundaryGap : false,
data : data['dates_metrics_date'],
}
],
yAxis : [
{
type : 'value'
}
],
series : [
{
name: "{% trans 'Login count' %}",
type:'line',
smooth: true,
itemStyle: {normal: {areaStyle: {type: 'default'}}},
data: data['dates_metrics_total_count_login']
},
{
name: "{% trans 'Active users' %}",
type: 'line',
smooth: true,
itemStyle: {normal: {areaStyle: {type: 'default'}}},
data: data['dates_metrics_total_count_active_users']
},
{
name:"{% trans 'Active assets' %}",
type:'line',
smooth:true,
itemStyle: {normal: {areaStyle: {type: 'default'}}},
data: data['dates_metrics_total_count_active_assets']
}
]
};
monthMetricsECharts.setOption(option);
}
);
}
function requireMonthTotalCountUsersPie(data){
require(
[
'echarts',
'echarts/chart/pie'
],
function (ec) {
var monthTotalCountUsersPie = ec.init(document.getElementById('dates_total_count_users_pie'));
var option = {
tooltip : {
trigger: 'item',
formatter: "{b} <br> {c} <br> ({d}%)"
},
legend: {
show: false,
orient : 'vertical',
x : 'left',
data:["{% trans 'Monthly active users' %}", "{% trans 'Disable user' %}", "{% trans 'Month not logged in user' %}"]
},
toolbox: {
show : false,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {
show: true,
type: ['pie', 'funnel'],
option: {
funnel: {
x: '25%',
width: '50%',
funnelAlign: 'center',
max: 1548
}
}
},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
series : [
{
name:"{% trans 'Access to the source' %}",
type:'pie',
radius : ['50%', '70%'],
avoidLabelOverlap: false,
itemStyle : {
normal : {
label : {
show : false
},
labelLine : {
show : false
}
},
emphasis : {
label : {
show : true,
position : 'center',
textStyle : {
fontSize : '5',
fontWeight : 'bold'
}
}
}
},
data:[
{value:data['dates_total_count_active_users'], name:"{% trans 'Monthly active users' %}"},
{value:data['dates_total_count_disabled_users'], name:"{% trans 'Disable user' %}"},
{value:data['dates_total_count_inactive_users'], name:"{% trans 'Month not logged in user' %}"}
]
}
]
};
monthTotalCountUsersPie.setOption(option);
}
);
}
function requireMonthTotalCountAssetsPie(data){
require(
[
'echarts',
'echarts/chart/pie'
],
function (ec) {
var monthTotalCountAssetsPie = ec.init(document.getElementById('dates_total_count_assets_pie'));
var option = {
tooltip : {
trigger: 'item',
formatter: "{b} <br> {c} <br> ({d}%)"
},
legend: {
show: false,
orient : 'vertical',
x : 'left',
data:["{% trans 'Month is logged into the asset' %}", "{% trans 'Disable host' %}", "{% trans 'Month not logged on host' %}"]
},
toolbox: {
show : false,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {
show: true,
type: ['pie', 'funnel'],
option: {
funnel: {
x: '25%',
width: '50%',
funnelAlign: 'center',
max: 1548
}
}
},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
series : [
{
name:"{% trans 'Access to the source' %}",
type:'pie',
radius : ['50%', '70%'],
itemStyle : {
normal : {
label : {
show : false
},
labelLine : {
show : false
}
},
emphasis : {
label : {
show : true,
position : 'center',
textStyle : {
fontSize : '5',
fontWeight : 'bold'
}
}
}
},
data:[
{value:data['dates_total_count_active_assets'], name:"{% trans 'Month is logged into the host' %}"},
{value:data['dates_total_count_disabled_assets'], name:"{% trans 'Disable host' %}"},
{value:data['dates_total_count_inactive_assets'], name:"{% trans 'Month not logged on host' %}"}
]
}
]
};
monthTotalCountAssetsPie.setOption(option);
}
);
}
var indexUrl = "/api/v1/index/";
function renderRequestApi(query, success, error){
var url = indexUrl + "?" + query;
if (!error){
error = function (){console.log("Request url error: " + url)}
}
requestApi({
url: url,
method: "GET",
success: success,
error: error,
flash_message: false,
})
}
function renderTotalCount(){
var success = function (data) {
$('#total_count_assets').html(data['total_count_assets']);
$('#total_count_users').html(data['total_count_users']);
$('#total_count_online_users').html(data['total_count_online_users']);
$('#total_count_online_sessions').html(data['total_count_online_sessions']);
};
renderRequestApi('total_count=1', success);
}
function renderMonthMetricsECharts(){
var success = function (data) {
requireMonthMetricsECharts(data)
};
renderRequestApi('dates_metrics=1', success)
}
function renderMonthTotalCountUsersPie(){
var success = function (data) {
requireMonthTotalCountUsersPie(data)
};
renderRequestApi('dates_total_count_users=1', success)
}
function renderMonthTotalCountAssetsPie(){
var success = function (data) {
requireMonthTotalCountAssetsPie(data)
};
renderRequestApi('dates_total_count_assets=1', success)
}
function renderWeekTotalCount(){
var success = function (data) {
$('#dates_total_count_login_users').html(data['dates_total_count_login_users']);
$('#dates_total_count_login_times').html(data['dates_total_count_login_times'])
};
renderRequestApi('dates_total_count=1', success)
}
function renderWeekLoginTimesTop5Users(){
var success = function (data){
var html = "";
var html_cell = "" +
"<li class=\"list-group-item fist-item\">" +
"<span class=\"pull-right\">" +
"{TOTAL} {% trans ' times/week' %}" +
"</span>" +
"<span class=\"label \">{INDEX}</span> {USER}" +
"</li>";
$.each(data['dates_login_times_top5_users'], function(index, value){
html += html_cell.replace('{TOTAL}', value['total'])
.replace('{USER}', value['user'])
.replace('{INDEX}', index+1)
});
$('#dates_login_times_top5_users').html(html)
};
renderRequestApi('dates_login_times_top5_users=1', success)
}
function renderWeekLoginTimesTop10Assets(){
var success = function (data){
var html = "";
var html_cell = "" +
"<div class=\"timeline-item\">" +
"<div class=\"row\">" +
"<div class=\"col-xs-5 date ellipsis\">" +
"<i class=\"fa fa-info-circle\"></i>" +
"<strong data-toggle=\"tooltip\" title=\"{ASSET}\">{ASSET}</strong>" +
"<br/>" +
"<small class=\"text-navy\">{TOTAL}{% trans ' times' %}</small>" +
"</div>" +
"<div class=\"col-xs-7 content no-top-border\">" +
"<p class=\"m-b-xs\">{% trans 'The time last logged in' %}</p>" +
"<p>{% trans 'At' %} {DATE_LAST}</p>" +
"</div>" +
"</div>" +
"</div>";
var assets = data['dates_login_times_top10_assets'];
if (assets.length !== 0){
$.each(assets, function(index, value){
html += html_cell
.replaceAll('{ASSET}', value['asset'])
.replace('{TOTAL}', value['total'])
.replace('{DATE_LAST}', toSafeLocalDateStr(value['last']))
});
}
else{
html += "<p class=\"text-center\">{% trans '(No)' %}</p>"
}
$('#dates_login_times_top10_assets').html(html)
};
renderRequestApi('dates_login_times_top10_assets=1', success)
}
function renderWeekLoginTimesTop10Users(){
var success = function (data){
var html = "";
var html_cell = "" +
"<div class=\"timeline-item\">" +
"<div class=\"row\">" +
"<div class=\"col-xs-5 date ellipsis\">" +
"<i class=\"fa fa-info-circle\"></i>" +
"<strong data-toggle=\"tooltip\" title=\"{USER}\">{USER}</strong>" +
"<br/>" +
"<small class=\"text-navy\">{TOTAL}{% trans ' times' %}</small>" +
"</div>" +
"<div class=\"col-xs-7 content no-top-border\">" +
"<p class=\"m-b-xs\">{% trans 'The time last logged in' %}</p>" +
"<p>{% trans 'At' %} {DATE_LAST}</p>" +
"</div>" +
"</div>" +
"</div>";
var users = data['dates_login_times_top10_users'];
if (users.length !== 0){
$.each(users, function(index, value){
html += html_cell.replaceAll('{USER}', value['user'])
.replace('{TOTAL}', value['total'])
.replace('{DATE_LAST}', toSafeLocalDateStr(value['last']))
});
}
else{
html += "<p class=\"text-center\">{% trans '(No)' %}</p>"
}
$('#dates_login_times_top10_users').html(html)
};
renderRequestApi('dates_login_times_top10_users=1', success)
}
function renderWeekLoginRecordTop10Sessions(){
var success = function (data){
var html = "";
var html_cell = "" +
"<div class=\"feed-element\">" +
"<a href=\"#\" class=\"pull-left\">" +
"<img alt=\"image\" class=\"img-circle\" src=\"{% static 'img/avatar/user.png' %}\">" +
"</a>" +
"<div class=\"media-body \">" +
"<small class=\"pull-right {TEXT_NAVY}\">{TIMESINCE} {% trans 'Before' %}</small>" +
"<strong>{USER}</strong> {% trans 'Login in ' %}{ASSET} <br>" +
"<small class=\"text-muted\">{DATE_START}</small>" +
"</div>" +
"</div>";
var users = data['dates_login_record_top10_sessions'];
if (users.length !== 0){
$.each(users, function(index, value){
console.log(value['is_finished'])
html += html_cell.replaceAll('{USER}', value['user'])
.replace('{ASSET}', value['asset'])
.replace('{DATE_START}', toSafeLocalDateStr(value['date_start']))
.replace('{TEXT_NAVY}', value['is_finished']?'':'text-navy')
.replace('{TIMESINCE}', value['timesince'])
});
}
else{
html += "<p class=\"text-center\">{% trans '(No)' %}</p>"
}
$('#dates_login_record_top10_sessions').html(html)
};
renderRequestApi('dates_login_record_top10_sessions=1', success)
}
function renderData(){
renderTotalCount();
renderMonthMetricsECharts();
renderMonthTotalCountUsersPie();
renderMonthTotalCountAssetsPie();
renderWeekTotalCount();
renderWeekLoginTimesTop5Users();
renderWeekLoginTimesTop10Assets();
renderWeekLoginRecordTop10Sessions();
renderWeekLoginTimesTop10Users();
}
require.config({
paths: {
'echarts': '/static/js/plugins/echarts/chart/',
'echarts/chart/line': '/static/js/plugins/echarts/chart/line',
'echarts/chart/pie': '/static/js/plugins/echarts/chart/pie'
}
});
$(document).ready(function(){
$('#show').click(function(){
$('#show').css('display', 'none');
$('#more').css('display', 'block');
});
$("[data-toggle='tooltip']").tooltip();
renderData()
});
</script>
{% endblock %}

View File

@ -42,10 +42,9 @@ class MySessionAPIView(generics.ListAPIView):
serializer_class = serializers.SessionSerializer serializer_class = serializers.SessionSerializer
def get_queryset(self): def get_queryset(self):
with tmp_to_root_org(): user = self.request.user
user = self.request.user qs = Session.objects.filter(user_id=user.id)
qs = Session.objects.filter(user_id=user.id) return qs
return qs
class SessionViewSet(OrgBulkModelViewSet): class SessionViewSet(OrgBulkModelViewSet):

View File

@ -24,10 +24,11 @@ class TicketSessionApi(views.APIView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
with tmp_to_root_org(): with tmp_to_root_org():
ticketsession = TicketSession.objects.filter(ticket=self.kwargs['ticket_id']).first() tid = self.kwargs['ticket_id']
if not ticketsession: ticket_session = TicketSession.objects.filter(ticket=tid).first()
if not ticket_session:
return Response(status=status.HTTP_404_NOT_FOUND) return Response(status=status.HTTP_404_NOT_FOUND)
session = ticketsession.session session = ticket_session.session
serializer = SessionSerializer(session) serializer = SessionSerializer(session)
return Response(serializer.data) return Response(serializer.data)

View File

@ -1,26 +0,0 @@
{% 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

@ -1,222 +0,0 @@
{% load i18n %}
<div class="col-lg-3" style="padding-left: 0" id="split-left">
<div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0">
<div class="file-manager ">
<div id="assetTree" class="ztree">
{% trans 'Loading' %} ...
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<div class="col-lg-9 animated fadeInRight" id="split-right">
<div class="tree-toggle">
<div class="btn btn-sm btn-primary tree-toggle-btn" onclick="toggle()">
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
</div>
</div>
<div class="mail-box-header">
<table class="table table-striped table-bordered table-hover" id="user_assets_table" >
<thead>
<tr>
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
<th class="text-center">{% trans 'Hostname' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
{% if show_actions %}
<th class="text-center">{% trans 'Action' %}</th>
{% endif %}
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<script>
var zTree;
var inited = false;
var url;
var assetTable;
var treeUrl = "NeedInput";
var assetTableUrl = 'NeedInput';
var selectUrl = 'NeedInput';
var systemUsersUrl = "NeedInput";
var showAssetHref = true; // Need input default true
var actions = {};
var labels = '';
var requesting = false;
function initTable() {
if (inited){
return assetTable
} else {
inited = true;
}
var options = {
ele: $('#user_assets_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
cellData = htmlEscape(cellData);
var assetDetailUrl = '{% url 'assets:asset-detail' pk=DEFAULT_PK %}'
.replace("{{ DEFAULT_PK }}", rowData.id);
var detailBtn = '<a href="assetDetailUrl" class="asset-detail" data-asset="assetId">' + cellData + '</a>';
if (showAssetHref) {
cellData = detailBtn.replace("assetDetailUrl", assetDetailUrl);
} else {
detailBtn = detailBtn.replace("assetId", rowData.id);
cellData = detailBtn.replace("assetDetailUrl", "");
}
$(td).html(cellData);
}},
{targets: 3, createdCell: function (td, cellData) {
var innerHtml = '<a class="btn-show-system-users" data-aid="99999999"> {% trans "Show" %} </a>'
.replace('99999999', cellData);
$(td).html(innerHtml);
}},
],
ajax_url: assetTableUrl,
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "id", orderable: false},
{% if show_actions %}
{data: "id", orderable: false}
{% endif %}
]
};
{% if show_actions %}
options.columnDefs.push(actions);
{% endif %}
assetTable = jumpserver.initServerSideDataTable(options);
return assetTable
}
function onSelected(event, treeNode) {
var node_id = treeNode.meta.node.id;
url = selectUrl.replace("{{ DEFAULT_PK }}", node_id);
assetTable.ajax.url(url);
assetTable.ajax.reload();
}
function initTree(refresh) {
var asyncUrl = setUrlParam(treeUrl, 'cache_policy', '1');
var setting = {
view: {
dblClickExpand: false,
showLine: true
},
data: {
simpleData: {
enable: true
}
},
async: {
enable: true,
url: asyncUrl,
autoParam: ["id=key", "name=n", "level=lv"],
type: 'get'
},
callback: {
onSelected: onSelected
}
};
$.get(treeUrl, function(data, status) {
if (data.length === 0) {
data.push({"name": "{% trans 'empty' %}", "id": ""})
}
zTree = $.fn.zTree.init($("#assetTree"), setting, data);
if (!refresh) {
initTable();
}
rootNodeAddDom(zTree, function () {
treeUrl = setUrlParam(treeUrl, 'cache_policy', '2');
initTree(true);
});
});
}
function getGrantedAssetSystemUsers(assetID, success) {
var url = systemUsersUrl.replace("{{ DEFAULT_PK }}", assetID);
requestApi({
url: url,
method: "GET",
success: success,
flash_message: false
})
}
function loadLabels() {
var labelListUrl = '{% url "api-assets:label-list" %}';
var label = '<li><a style="font-weight: bolder">labelName:labelValue</a></li>';
if (requesting) {
return
}
if (!labels) {
var data = {
url: labelListUrl,
method: "GET",
success: function (data) {
data.forEach(function (value) {
labels += label.replace("labelName", value.name).replace("labelValue", value.value)
});
$(".labels-menu").append(labels);
requesting = false;
},
error: function() {
requesting = false;
},
flash_message: false
};
requesting = true;
requestApi(data)
}
}
var show = 0;
function toggle() {
if (show === 0) {
$("#split-left").hide(500, function () {
$("#split-right").attr("class", "col-lg-12");
$("#toggle-icon").attr("class", "fa fa-angle-right fa-x");
show = 1;
});
} else {
$("#split-right").attr("class", "col-lg-9");
$("#toggle-icon").attr("class", "fa fa-angle-left fa-x");
$("#split-left").show(500);
show = 0;
}
setTimeout(function () {
$(".table").css("width", "100%");
{#assetTable.columns.adjust();#}
}, 500)
}
$(document).ready(function () {
{#loadLabels()#}
}).on('click', '.labels-menu li', function () {
var val = $(this).text();
$("#user_assets_table_filter input").val(val);
assetTable.search(val).draw();
}).on('click', '.btn-show-system-users', function () {
var $this = $(this);
var assetId = $this.data('aid');
function success(systemUsers) {
var users = [];
$.each(systemUsers, function (id, data) {
var name = htmlEscape(data.name);
users.push(name);
});
$this.parent().html(users.join(','))
}
getGrantedAssetSystemUsers(assetId, success)
})
</script>

View File

@ -1,23 +0,0 @@
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_class %}modal-lg{% endblock %}
{% block modal_id %}select_user_modal{% endblock %}
{% block modal_title%}{% trans "Please Select User" %}{% endblock %}
{% block modal_body %}
<table class="table table-striped table-bordered table-hover " id="select_user_table" >
<thead>
<tr>
<th class="text-center">
<div class="checkbox checkbox-default"><input id="" type="checkbox" class="ipt_check_all"><label></label></div>
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Role' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
</tr>
</thead>
</table>
{% endblock %}
{% block modal_confirm_id %}btn_select_user{% endblock %}

View File

@ -1,97 +0,0 @@
{% 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,8 +0,0 @@
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}user_update_pk_modal{% endblock %}
{% block modal_title%}{% trans "Update User SSH Public Key" %}{% endblock %}
{% block modal_body %}
<textarea id="txt_pk" class="form-control" cols="30" rows="10" placeholder="ssh-rsa AAAAB3NzaC1yc2EAA....."></textarea>
{% endblock %}
{% block modal_confirm_id %}btn_user_update_pk{% endblock %}

View File

@ -1,10 +0,0 @@
{% extends '_base_only_content.html' %}
{% load static %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %} {% trans 'First Login' %} {% endblock %}
{% block content %}
使用UI重构这个页面
{% endblock %}

View File

@ -1,54 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% load bootstrap3 %}
{% block custom_head_css_js %}
{{ wizard.form.media }}
<link href="{% static 'css/plugins/steps/jquery.steps.css' %}" rel="stylesheet">
{% endblock %}
{% block first_login_message %}{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox">
<div class="ibox-title">
<h5>{% trans 'First Login' %}</h5>
<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>
</div>
</div>
<div class="ibox-content">
<div class="alert alert-success" id="messages">
{% trans 'Welcome to use jumpserver, visit ' %}
<a href="{{ user_guide_url }}" target="_blank">{% trans 'Use guide' %}</a> {% trans ' for more information' %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).on('click', ".fl_goto", function(){
var $form = $('#fl_form');
$('<input />', {'name': 'wizard_goto_step', 'value': $(this).data('goto'), 'type': 'hidden'}).appendTo($form);
$form.submit();
return false;
}).on('click', '#fl_submit', function(){
$('#fl_form').submit();
return false;
})
</script>
{% endblock %}

View File

@ -1,201 +0,0 @@
{% 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 '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' %}
{% endblock %}
{% block custom_foot_js %}
<script>
jumpserver.nodes_selected = {};
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.assets.length > 0) {
data += makeLabel(["{% trans 'Asset' %}", d.assets.join(", ")])
}
if (d.nodes.length > 0) {
data += makeLabel(["{% trans 'Node' %}", d.nodes.join(", ")])
}
if (d.system_users.length > 0) {
data += makeLabel(["{% trans 'System user' %}", d.system_users.join(", ")])
}
if (d.actions.length > 0) {
data += makeLabel(["{% trans 'Action' %}", d.actions.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:asset-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) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 7, 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: 8, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(rowData.name);
var update_btn = '<a href="{% url "perms:asset-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);
if (rowData.inherit) {
del_btn = del_btn.replace("mark", "disabled")
}
$(td).html(update_btn + del_btn);
}}
],
ajax_url: '{% url "api-perms:asset-permission-list" %}?user_id={{ object.id }}',
columns: [
{data: "id"}, {data: "name"}, {data: "users", orderable: false},
{data: "user_groups", orderable: false}, {data: "assets", orderable: false},
{data: "nodes", 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();
var filterMenu = [
{title: "{% trans 'Name' %}", value: "name"},
{title: "{% trans 'Validity' %}", value: "is_valid"},
{title: "{% trans 'IP' %}", value: "ip"},
{title: "{% trans 'Hostname' %}", value: "hostname"},
{title: "{% trans 'Node' %}", value: "node"},
{title: "{% trans 'System user' %}", value: "system_user"},
{title: "{% trans 'Inherit' %}", value: "all", submenu: [
{title: "{% trans 'Include' %}", value: "1"},
{title: "{% trans 'Exclude' %}", value: "0"},
]},
];
initTableFilterDropdown('#permission_list_table_filter input', filterMenu, 15, 38)
})
.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:asset-permission-detail" pk=DEFAULT_PK %}'
.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
})
</script>
{% endblock %}

View File

@ -1,168 +0,0 @@
{% 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,134 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% load bootstrap3 %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/cropper/cropper.min.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
<script type="text/javascript" src="{% static 'js/pwstrength-bootstrap.js' %}"></script>
<script src="{% static "js/jumpserver.js" %}"></script>
<style>
.crop {
width: 200px;
height: 150px;
overflow: hidden;
}
.img-preview-sm img {
width: 64px;
height: 64px;
margin: -75px 0 0 -100px;
}
img {
max-width: 100%; /* This rule is very important, please do not ignore this! */
}
</style>
{% 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>
<a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a>
</li>
{% if request.user.can_update_password %}
<li class="active">
<a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a>
</li>
{% endif %}
{% if request.user.can_update_ssh_key %}
<li>
<a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a>
</li>
{% endif %}
</ul>
</div>
<div class="tab-content" style="background-color: #ffffff">
<div class="wrapper wrapper-content animated fadeInRight">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_field form.old_password layout="horizontal" %}
{% bootstrap_field form.new_password layout="horizontal" %}
{# 密码popover #}
<div id="container">
<div class="popover fade bottom in" role="tooltip" id="popover777" style=" display: none; width:260px;">
<div class="arrow" style="left: 50%;"></div>
<h3 class="popover-title" style="display: none;"></h3>
<h4>{% trans 'Your password must satisfy' %}</h4><div id="id_password_rules" style="color: #908a8a; margin-left:20px; font-size:15px;"></div>
<h4 style="margin-top: 10px;">{% trans 'Password strength' %}</h4><div id="id_progress"></div>
<div class="popover-content"></div>
</div>
</div>
{% bootstrap_field form.confirm_password layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/cropper/cropper.min.js' %}"></script>
<script>
$(document).ready(function () {
var el = $('#id_password_rules'),
idPassword = $('#id_new_password'),
idPopover = $('#popover777'),
container = $('#container'),
progress = $('#id_progress'),
password_check_rules = {{ password_check_rules|safe }},
minLength = 6,
top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) - 10 + 34,
left = 377,
i18n_fallback = {
"veryWeak": "{% trans 'Very weak' %}",
"weak": "{% trans 'Weak' %}",
"normal": "{% trans 'Normal' %}",
"medium": "{% trans 'Medium' %}",
"strong": "{% trans 'Strong' %}",
"veryStrong": "{% trans 'Very strong' %}"
};
jQuery.each(password_check_rules, function (idx, rules) {
if(rules.key === 'id_security_password_min_length'){
minLength = rules.value
}
});
// 初始化popover
initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback);
// 监听事件
idPassword.on('focus', function () {
idPopover.css('top', top);
idPopover.css('left', left);
idPopover.css('display', 'block');
});
idPassword.on('blur', function () {
idPopover.css('display', 'none');
});
idPassword.on('keyup', function(){
var password = idPassword.val();
checkPasswordRules(password, minLength);
});
})
</script>
{% endblock %}

View File

@ -46,8 +46,6 @@ def get_user_or_pre_auth_user(request):
def redirect_user_first_login_or_index(request, redirect_field_name): def redirect_user_first_login_or_index(request, redirect_field_name):
# if request.user.is_first_login:
# return reverse('authentication:user-first-login')
url_in_post = request.POST.get(redirect_field_name) url_in_post = request.POST.get(redirect_field_name)
if url_in_post: if url_in_post:
return url_in_post return url_in_post

View File

@ -21,7 +21,7 @@ from ... import forms
__all__ = [ __all__ = [
'UserLoginView', 'UserResetPasswordView', 'UserForgotPasswordView', 'UserFirstLoginView', 'UserLoginView', 'UserResetPasswordView', 'UserForgotPasswordView',
] ]
@ -130,8 +130,3 @@ class UserResetPasswordView(FormView):
'auto_redirect': True, 'auto_redirect': True,
} }
return FlashMessageUtil.gen_message_url(message_data) return FlashMessageUtil.gen_message_url(message_data)
class UserFirstLoginView(PermissionsMixin, TemplateView):
template_name = 'users/first_login.html'
permission_classes = [IsValidUser]