first sync
Some checks failed
Deployment Verification / deploy-and-test (push) Failing after 29s

This commit is contained in:
2025-03-04 07:59:21 +01:00
parent 9cdcf486b6
commit 506716e703
1450 changed files with 577316 additions and 62 deletions

View File

@ -0,0 +1,125 @@
{% extends "layouts/default.html" %}
{% block title %} Access Control {% endblock title %}
{% block stylesheets %}
<link rel="stylesheet" href="/static/assets/css/suggestags.css">
<link rel="stylesheet" href="/static/assets/css/bootstrap-multiselect.min.css">
{% endblock stylesheets %}
{% block content %}
{% if current_user.is_authenticated %}
{{ form.hidden_tag() }}
<div class="page-inner">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-heading collapsed" href="#collapse_user_mgmt" title="Click to unfold" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapse_user_mgmt">
<span class="accicon float-left mr-3"><i class="fas fa-angle-right rotate-icon"></i></span>
<div class="card-title">Users</div>
</div>
<div class="col">
<button type="button" class="btn btn-sm btn-dark float-right ml-2" onclick="refresh_users(true);">Refresh</button>
<a class="btn btn-sm btn-dark float-right ml-2" href="access-control/audit/users?cid={{ session['current_case'].case_id }}">Audit users</a>
<button class="btn btn-sm btn-dark float-right ml-2" onclick="add_user();">Add user</button>
</div>
</div>
</div>
<div class="card-body collapse" id="collapse_user_mgmt">
<div class="table-responsive" id="users_table_wrapper">
<div class="selectgroup">
<span id="table_buttons"></span>
</div>
<table class="table display table table-striped table-hover" width="100%"
cellspacing="0" id="users_table">
<thead>
<tr>
<th>#ID</th>
<th>Name</th>
<th>Login Name</th>
<th>Email</th>
<th>Active</th>
<th>Service Account</th>
</tr>
</thead>
<tfoot>
<tr>
<th>#ID</th>
<th>Name</th>
<th>Login Name</th>
<th>Email</th>
<th>Active</th>
<th>Service Account</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-heading collapsed" href="#collapse_groups_mgmt" title="Click to unfold" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapse_groups_mgmt">
<span class="accicon float-left mr-3"><i class="fas fa-angle-right rotate-icon"></i></span>
<div class="card-title">Groups</div>
</div>
<div class="col">
<button type="button" class="btn btn-sm btn-dark float-right ml-2" onclick="refresh_groups(true);">Refresh</button>
<button class="btn btn-sm btn-dark float-right" onclick="add_group();">Add group</button>
</div>
</div>
</div>
<div class="card-body collapse" id="collapse_groups_mgmt">
<div class="table-responsive" id="groups_table_wrapper">
<div class="selectgroup">
<span id="groups_table_buttons"></span>
</div>
<table class="table display table table-striped table-hover" width="100%"
cellspacing="0" id="groups_table">
<thead>
<tr>
<th>#ID</th>
<th>Name</th>
<th>Description</th>
<th>Permissions</th>
<th>#Members</th>
</tr>
</thead>
<tfoot>
<tr>
<th>#ID</th>
<th>Name</th>
<th>Description</th>
<th>Permissions</th>
<th>#Members</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<div class="modal" tabindex="-1" role="dialog" id="modal_access_control" data-backdrop="true">
</div>
<div class="modal bg-shadow-gradient" tabindex="-1" role="dialog" id="modal_ac_additional" data-backdrop="true">
</div>
{% endblock content %}
{% block javascripts %}
<script src="/static/assets/js/plugin/select/bootstrap-multiselect.min.js"></script>
<script src="/static/assets/js/plugin/datatables/dataTables.select.min.js"></script>
<script src="/static/assets/js/plugin/datatables/dataTables.contextualActions.min.js"></script>
<script src="/static/assets/js/iris/manage.users.js"></script>
<script src="/static/assets/js/iris/manage.cases.common.js"></script>
<script src="/static/assets/js/iris/manage.groups.js"></script>
{% endblock javascripts %}

View File

@ -0,0 +1,58 @@
{% extends "layouts/default.html" %}
{% block title %} Access Control {% endblock title %}
{% block stylesheets %}
<link rel="stylesheet" href="/static/assets/css/suggestags.css">
<link rel="stylesheet" href="/static/assets/css/bootstrap-select.min.css">
<link rel="stylesheet" href="/static/assets/css/select2.css">
{% endblock stylesheets %}
{% block content %}
{{ form.hidden_tag() }}
<div class="page-inner">
<div class="row">
<div class="col-md-12">
<a class="mb-2 ml-1 text-dark" href="/manage/access-control?cid={{ session['current_case'].case_id }}"><i class="fa-solid fa-arrow-left"></i> Back</a>
<div class="card mt-2">
<div class="card-header">
<div class="row">
<div class="col col-heading">
<div class="card-title">Users audit</div>
</div>
</div>
</div>
<div class="card-body" id="">
<div class="row">
<div class="col-12">
<div class="input-group mb-3">
<select id="users_audit_select" name="users_audit_select" class="form-control ml-12"
tabindex="-1" style="width: 50%"></select>
<div class="input-group-append">
<button type="button" class="btn btn-outline-success" id="get_user_audit_btn"
onclick="get_user_audit_page();">Audit</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
</div>
</div>
</div>
</div>
<div id="user_audit_content">
</div>
</div>
</div>
</div>
{% endblock content %}
{% block javascripts %}
<script src="/static/assets/js/plugin/datatables/dataTables.select.min.js"></script>
<script src="/static/assets/js/iris/manage.audit.users.js"></script>
<script src="/static/assets/js/iris/datatablesUtils.js"></script>
{% endblock javascripts %}

View File

@ -0,0 +1,292 @@
<div class="modal-xl modal-dialog" role="document">
<div class="modal-content" id="modal_access_control_content">
<div class="modal-header">
<h4 class="modal-title mt-2 mr-4">{% if not group.group_id %}Add Group {% else %} Edit {{ group.group_name }} {% endif %}</h4>
<div class="row text-center mr-4">
{% if group.group_id %}
<ul class="nav nav-pills nav-default mr-4" role="tablist">
<li class="nav-item">
<a class="nav-link active show" data-toggle="pill" href="#group_details_tab" role="tab" aria-controls="group_details_tab" aria-selected="false">Info</a>
</li>
<li class="nav-item submenu">
<a class="nav-link" data-toggle="pill" href="#group_members_tab" role="tab" aria-controls="group_members_tab" aria-selected="false">Members</a>
</li>
<li class="nav-item submenu">
<a class="nav-link" data-toggle="pill" href="#group_cac_tab" role="tab" aria-controls="group_cac_tab" aria-selected="false">Cases access</a>
</li>
</ul>
{% endif %}
</div>
<div class="row text-right">
<button type="button" class="pull-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
</div>
<div class="modal-body">
<div role="tabpanel">
<div class="tab-content">
<div class="tab-pane active" id="group_details_tab">
<div class="container col-md-12" >
<form method="post" action="" id="form_new_group">
<div class="col-md-12 col-lg-12 col-sm-12">
{{ form.hidden_tag() }}
{% if not group.group_id %}
<p class="ml-3"><i class="fa-solid fa-circle-info mr-2"></i>Members can be added once the group is created.</p>
{% endif %}
<div class="form-group">
<label for="group_name" class="mr-4">Group name *
</label>
{{ form.group_name(class='form-control', autocomplete="off") }}
</div>
<div class="form-group mt-3">
<label for="group_description" class="placeholder">Description *</label>
{{ form.group_description(class='form-control', autocomplete="off") }}
</div>
<div class="form-group" data-select2-id="7">
<label>Permissions *</label>
<div class="select2-input ml-12" data-select2-id="6">
<select id="group_permissions" name="group_permissions" class="form-control select2-hidden-accessible ml-12" multiple="" data-select2-id="group_permissions" tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
</div>
</div>
</div>
{% if group.group_id %}
<button type="button" class="btn btn-danger mt-5"
onclick="delete_group('{{ group.group_id }}');">Delete</button>
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right"
id="submit_new_group">Update</button>
{% else %}
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right"
id="submit_new_group">Save</button>
{% endif %}
</form>
</div>
</div>
<div class="tab-pane" id="group_members_tab">
<div class="container col-md-12" >
<div class="row d-flex">
<div class="col pull-right">
<button class="btn btn-dark btn-sm pull-right" onclick="refresh_group_members({{ group.group_id }});">
<span class="menu-title">Refresh</span>
</button>
<button class="btn btn-dark btn-sm pull-right mr-2" onclick="add_members_to_group({{ group.group_id }});">
<span class="menu-title">Manage</span>
</button>
</div>
</div>
<div class="row">
<table class="table display table-bordered table-striped table-hover responsive" width="100%" cellspacing="0" id="group_members_table" >
<thead>
<tr>
<th>User ID</th>
<th>User login</th>
<th>User display name</th>
</tr>
</thead>
<tfoot>
<tr>
<th>User ID</th>
<th>User login</th>
<th>User display name</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<div class="tab-pane" id="group_cac_tab">
<div class="container col-md-12" >
<div class="row d-flex mb-2">
<div class="col">
{% if group.group_auto_follow %}
<b><i class="fa fa-triangle-exclamation text-warning mr-2"></i>This group is set to automatically include all new cases.</b>
{% endif %}
</div>
<div class="col pull-right">
<button class="btn btn-dark btn-sm pull-right" onclick="refresh_group_cac({{ group.group_id }});">
<span class="menu-title">Refresh</span>
</button>
<button class="btn btn-dark btn-sm pull-right mr-2" onclick="manage_group_cac({{ group.group_id }});">
<span class="menu-title" id="manage_group_cac_button">Set case access</span>
</button>
</div>
</div>
<div class="row mt-2">
<table class="table display table-bordered table-striped table-hover responsive" width="100%" cellspacing="0" id="group_cac_table" >
<thead>
<tr>
<th>Case ID</th>
<th>Case Name</th>
<th>Access</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Case ID</th>
<th>Case Name</th>
<th>Access</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
var data = [
{% for e in all_perms %}
{
value: {{ e.value }},
label: "{{ e.name }}"
}
{% if not loop.last %},{% endif %}
{% endfor %}
];
$('#group_permissions').multiselect({
buttonWidth: 400,
nonSelectedText: 'Select permissions',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#group_permissions').multiselect('dataprovider', data );
{% if group.group_permissions_list %}
$('#group_permissions').multiselect('select', [
{% for perm in group.group_permissions_list %} {{ perm.value }}, {% endfor %}
]);
$('#org_members').multiselect('refresh')
{% endif %}
var modal_group_table = $("#group_members_table").DataTable({
dom: 'Blfrtip',
aaData: [],
aoColumns: [
{
"data": "id",
"render": function ( data, type, row ) {
return `<i class="fa-solid fa-trash-can mr-2 text-danger" style="cursor:pointer;" title="Remove from group" href="javascript:void(0)" onclick="remove_members_from_group('{{ group.group_id }}',${data})"></i>${data}`;
},
"className": "dt-center"
},
{
"data": "user",
"className": "dt-center",
"render": function (data, type, row) {
return sanitizeHTML(data);
}
},
{
"data": "name",
"className": "dt-center",
"render": function (data, type, row) {
return sanitizeHTML(data);
}
}
],
filter: true,
info: true,
ordering: true,
processing: true
});
{% if group.group_id %}
modal_group_table.rows.add({{ group.group_members|tojson }});
modal_group_table.columns.adjust().draw();
{% endif %}
var modal_group_cac_table = $("#group_cac_table").DataTable({
dom: 'Blfrtip',
aaData: [],
aoColumns: [
{
"data": "case_id",
"render": function ( data, type, row ) {
return `<i class="fa-solid fa-trash-can mr-2 text-danger" style="cursor:pointer;" title="Remove access to case" href="javascript:void(0)" onclick="remove_case_access_from_group('{{ group.group_id }}',${data})"></i>${data}`;
},
"className": "dt-center"
},
{
"data": "case_name",
"className": "dt-center",
"render": function (data, type, row) {
return `<a target="_blank" rel="noopener" href="/case?cid=${row.case_id}">${sanitizeHTML(data)}</a>`;
}
},
{
"data": "access_level_list",
"render": function ( data, type, row ) {
ret_data = "";
for (acc in data) {
ret_data += `<span class="badge ml-2 badge-light">${data[acc].name}</span>`
}
return ret_data;
},
"className": "dt-center"
}
],
filter: true,
info: true,
ordering: true,
processing: true,
select: true
});
var actionOptionsGroup = {
classes: [],
contextMenu: {
enabled: true,
isMulti: true,
xoffset: -10,
yoffset: -10,
headerRenderer: function (rows) {
if (rows.length > 1) {
return rows.length + ' items selected';
} else {
let row = rows[0];
return 'Quick action';
}
},
},
buttonList: {
enabled: false,
},
deselectAfterAction: true,
items: [],
};
{% if group.group_id %}
actionOptionsGroup.items.push({
type: 'option',
title: 'Remove access',
multi: true,
iconClass: 'fas fa-trash',
buttonClasses: ['btn', 'btn-outline-primary'],
action: function(rows){
remove_group_cases_from_group_table({{ group.group_id }}, rows);
}
});
modal_group_cac_table.contextualActions(actionOptionsGroup);
current_group_cases_access_list = {{ group.group_cases_access|tojson }};
modal_group_cac_table.rows.add(current_group_cases_access_list);
modal_group_cac_table.columns.adjust().draw();
{% endif %}
</script>

View File

@ -0,0 +1,90 @@
<div class="modal-lg modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title mt-2 mr-4">Set case access</h4>
<div class="row text-right">
<button type="button" class="pull-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
</div>
<div class="modal-body">
<div class="container col-md-12" >
<div class="row">
<div class="form-group" data-select2-id="7">
<label>Set cases access of group <i>{{ group.group_name }}</i> *</label>
<div class="row">
<div class="col-12">
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="enable_auto_follow_cases" name="enable_auto_follow_cases" {% if group.group_auto_follow %}checked{% endif %}>
<span class="form-check-sign">Apply to currents and futures cases</span>
</label>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<select id="group_case_access_select" name="org_case_access_select" class="form-control select2-hidden-accessible ml-12" multiple="multiple"
tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
<select id="group_case_ac_select" name="org_case_ac_select" class="form-control select2-hidden-accessible ml-12"
tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
</div>
</div>
</div>
</div>
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right"
id="grant_case_access_to_group">Set access</button>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
var data = [];
$('#group_case_access_select').multiselect({
buttonWidth: 400,
nonSelectedText: 'Select case',
emptyText: 'No case available to add',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#group_case_access_select').multiselect('dataprovider', [{% for ocs in outer_cases %}
{ label: "{{ ocs.case_name }}", value: {{ ocs.case_id }} }, {% endfor %}]);
$('#group_case_access_select').multiselect('refresh')
$('#group_case_ac_select').multiselect({
nonSelectedText: 'Select access level',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#group_case_ac_select').multiselect('dataprovider', [{% for acc in access_levels %}
{ label: "{{ acc.name }}", value: {{ acc.value }} }, {% endfor %}]);
$('#group_case_ac_select').multiselect('refresh');
$('#enable_auto_follow_cases').on('change', function() {
if (this.checked) {
$('#group_case_access_select').multiselect('disable');
} else {
$('#group_case_access_select').multiselect('enable');
}
});
if ($('#enable_auto_follow_cases').is(':checked')) {
$('#group_case_access_select').multiselect('disable');
}
</script>

View File

@ -0,0 +1,62 @@
<div class="modal-lg modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title mt-2 mr-4">Manage members of group {{ group.group_name }}</h4>
<div class="row text-right">
<button type="button" class="pull-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
</div>
<div class="modal-body">
<div class="container col-md-12" >
<div class="row">
<div class="form-group" data-select2-id="7">
<label>Members *</label>
<div class="select2-input ml-12" data-select2-id="6">
<select id="group_members" name="group_members" class="form-control select2-hidden-accessible ml-12" multiple=""
data-select2-id="group_members" tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
</div>
</div>
</div>
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right"
id="save_group_members">Save</button>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
var data = [];
if (current_users_list.length === 0) {
refresh_users();
}
for (user in current_users_list) {
data.push({
label: `${current_users_list[user].user_login} (${current_users_list[user].user_name} - ${current_users_list[user].user_email})`,
value: current_users_list[user].user_id
});
}
$('#group_members').multiselect({
buttonWidth: 400,
nonSelectedText: 'Select members',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#group_members').multiselect('dataprovider', data );
{% if group.group_members %}
$('#group_members').multiselect('select', [
{% for member in group.group_members %} {{ member.id }}, {% endfor %}
]);
$('#group_members').multiselect('refresh')
{% endif %}
</script>

View File

@ -0,0 +1,343 @@
<div class="modal-xl modal-dialog" role="document">
<div class="modal-content" id="modal_access_control_content">
<div class="modal-header">
<div class="row w-100 d-flex justify-content-center">
<h4 class="modal-title ml-4 mt-3">{% if not user.user_id %}Add User{% else %} Edit user {% endif %}</h4>
<div class="col">
{% if user.user_id %}
<ul class="nav nav-pills nav-default justify-content-center" role="tablist">
<li class="nav-item">
<a class="nav-link active show" data-toggle="pill" href="#user_details_tab" role="tab" aria-controls="user_details_tab" aria-selected="false">Info</a>
</li>
<li class="nav-item submenu">
<a class="nav-link" data-toggle="pill" href="#user_permissions_tab" role="tab" aria-controls="user_permissions_tab" aria-selected="false">Permissions</a>
</li>
<li class="nav-item submenu">
<a class="nav-link" data-toggle="pill" href="#user_groups_tab" role="tab" aria-controls="user_groups_tab" aria-selected="false">Groups</a>
</li>
<li class="nav-item submenu">
<a class="nav-link" data-toggle="pill" href="#user_cac_tab" role="tab" aria-controls="user_cac_tab" aria-selected="false">Cases access</a>
</li>
</ul>
{% endif %}
</div>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
</div>
</div>
<div class="modal-body">
<div role="tabpanel">
<div class="tab-content">
<div class="tab-pane active" id="user_details_tab">
<div class="container col-md-12">
<form method="post" action="" id="form_new_user">
<div class="col-md-12 col-lg-12 col-sm-12">
{{ form.hidden_tag() }}
{% if not user.user_id %}
<p class="ml-3"><i class="fa-solid fa-circle-info mr-2"></i>Permissions and groups memberships can be set once the user is created.</p>
{% endif %}
{% if user.user_is_service_account %}
<p class="ml-3 text-warning-high"><i class="fa-solid fa-circle-info mr-2"></i>This is a service account. It cannot login interactively nor have a password.</p>
{% endif %}
<div class="form-group">
<label for="user_name" class="mr-4">Full name
</label>
{{ form.user_name(class='form-control', autocomplete="off") }}
</div>
<div class="form-group mt-3">
<label for="user_login" class="placeholder">Login</label>
{{ form.user_login(class='form-control', autocomplete="off") }}
</div>
<div class="form-group mt-3">
<label for="user_email" class="placeholder">Email</label>
{{ form.user_email(class='form-control', autocomplete="off") }}
</div>
{% if not user.user_is_service_account %}
<div class="form-group mt-3" id="formGroupUserPassword">
<label for="user_pwd" class="placeholder">Password (optional for service accounts)</label>
<ul>
<li><small>Must contain at least {{ server_settings.password_policy_min_length }} chars</small></li>
{% if server_settings.password_policy_upper_case %}
<li class="text-sm"><small>Must contain at least an upper case</small></li>
{% endif %}
{% if server_settings.password_policy_lower_case %}
<li class="text-sm"><small>Must contain at least a lower case</small></li>
{% endif %}
{% if server_settings.password_policy_digit %}
<li class="text-sm"><small>Must contain at least a digit</small></li>
{% endif %}
{% if server_settings.password_policy_special_chars %}
<li class="text-sm"><small>Must contain at least one of : {{ server_settings.password_policy_special_chars }}</small></li>
{% endif %}
</ul>
<div class="input-group mb-3">
{{ form.user_password(class='form-control', autocomplete="off") }}
<div class="input-group-append">
<span class="input-group-text">
<div class="user_show_password" id="toggle_user_password"><i class="fa-solid fa-eye"></i></div>
</span>
</div>
</div>
</div>
{% endif %}
{% if not user.user_id %}
<div class="form-group mt-3">
<div class="form-check">
<label class="form-check-label mt-3" >
{{ form.user_is_service_account(class="form-check-input", type="checkbox") }}
<span class="form-check-sign" id="formCheckIsServiceAccount"> Use as service account
<i class="ml-1 mt-1 fa-regular fa-circle-question" title="If checked, the user won't appear in the attribution suggestions and won't be able to connect on the UI" style="cursor:pointer;"></i>
</span>
</label>
</div>
</div>
{% endif %}
{% if user.user_id %}
<div class="form-group mt-3">
<label for="user_id" class="placeholder">User ID</label>
<input autocomplete="off" class="form-control" type="text" value="{{ user.user_id }}" disabled>
</div>
<div class="form-group mt-3">
<label for="user_uuid" class="placeholder">User UUID</label>
<input autocomplete="off" class="form-control" type="text" value="{{ user.user_uuid }}" disabled>
</div>
<div class="form-group mt-3">
<label for="user_uuid" class="placeholder">User API Key</label>
<input autocomplete="off" class="form-control" type="text" value="{{ user.user_api_key }}" disabled>
</div>
{% endif %}
</div>
{% if user.user_id %}
<button type="button" class="btn btn-danger mt-5"
onclick="delete_user('{{ user.user_id }}');">Delete</button>
{% if user.user_active %}
<button type="button" class="btn btn-outline-danger mt-5"
onclick="deactivate_user('{{ user.user_id }}');">Deactivate</button>
{% else %}
<button type="button" class="btn btn-outline-success mt-5"
onclick="activate_user('{{ user.user_id }}');">Activate</button>
{% endif %}
<button type="button" class="btn btn-outline-success ml-1 mt-5 float-right"
id="submit_new_user">Update</button>
<button type="button" class="btn btn-dark mt-5 float-right"
onclick="refresh_user_ac('{{ user.user_id }}');" id="users_refresh_ac_btn">Refresh access</button>
{% else %}
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right"
id="submit_new_user">Save</button>
{% endif %}
</form>
</div>
</div>
<div class="tab-pane" id="user_permissions_tab">
<div class="container col-md-12">
<p class="mb-4"><i class="fa-solid fa-circle-info mr-2"></i>Permissions are inherited from the groups the user belongs to. The table shows the effective permissions the user has on the platform.</p>
<table class="table table-striped" id="user_permissions_table">
<thead>
<tr>
<th>Permission</th>
<th>Value</th>
<th>Inherited from groups</th>
</tr>
</thead>
<tbody>
{% for perm in user.user_permissions %}
<tr>
<td>{{ user.user_permissions[perm].name }}</td>
<td>0x{{ perm | int(perm,16) }}</td>
<td>{% for group in user.user_permissions[perm].inherited_from %}<span class="badge ml-2 badge-light">{{ group }}</span>{% endfor %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="tab-pane" id="user_groups_tab">
<div class="row">
<div class="container col-md-12">
<span class="ml-2 mt-3">Groups the user is member of.</span>
<button class="btn btn-dark btn-sm pull-right mr-2" onclick="manage_user_groups({{ user.user_id }});">
<span class="menu-title">Manage</span>
</button>
</div>
</div>
<div class="row mt-4">
<div class="container col-md-12">
<table class="table table-striped" id="user_groups_table">
<thead>
<tr>
<th>Group name</th>
<th>Group ID</th>
<th>Group UUID</th>
</tr>
</thead>
<tbody>
{% for group in user.user_groups %}
<tr>
<td>{{ group.group_name }}</td>
<td>{{ group.group_id }}</td>
<td>{{ group.group_uuid }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="tab-pane" id="user_cac_tab">
<div class="container col-md-12" >
<div class="row d-flex mb-4">
<div class="col-8">
<span>Cases accesses are usually inherited from groups memberships. These are not displayed here. This tab allows to add granular case access if necessary.</span>
</div>
<div class="col-4 pull-right">
<button class="btn btn-dark btn-sm pull-right" onclick="refresh_user_cac({{ user.user_id }});">
<span class="menu-title">Refresh</span>
</button>
<button class="btn btn-dark btn-sm pull-right mr-2" onclick="manage_user_cac({{ user.user_id }});">
<span class="menu-title" id="manage_user_cac_button">Set case access</span>
</button>
</div>
</div>
<div class="row mt-4">
<table class="table display table-striped table-hover responsive" width="100%" cellspacing="0" id="user_cac_table" >
<thead>
<tr>
<th>Case ID</th>
<th>Case Name</th>
<th>Access</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Case ID</th>
<th>Case Name</th>
<th>Access</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$('#toggle_user_password').on('click', function (e) {
const type = $('#user_password').attr('type') === 'password' ? 'text' : 'password';
$('#user_password').attr('type', type);
$('#toggle_user_password > i').attr('class', type === 'password' ? 'fa-solid fa-eye' : 'fa-solid fa-eye-slash');
});
$('#user_permissions_table').dataTable({
"order": [[ 1, "asc" ]]});
$('#user_groups_table').dataTable({
"order": [[ 1, "asc" ]],
"columns": [{
"title": "Group name",
"render": function ( data, type, row, meta ) {
if (type === 'display' ) {
return `<i class="fa-solid fa-trash-can mr-2 text-danger" style="cursor:pointer;" title="Remove from group" href="javascript:void(0)" onclick="remove_member_from_group_wrap('${row[1]}','{{ user.user_id }}')"></i>${sanitizeHTML(data)}`;
}
return data;
}},
{"title": "Group ID"},
{"title": "Group UUID"}
]
});
var modal_user_cac_table = $("#user_cac_table").DataTable({
dom: 'Blfrtip',
aaData: [],
aoColumns: [
{
"data": "case_id",
"render": function ( data, type, row ) {
return `<i class="fa-solid fa-trash-can mr-2 text-danger" style="cursor:pointer;" title="Remove access to case" href="javascript:void(0)" onclick="remove_case_access_from_user('{{ user.user_id }}',${data})"></i>${data}`;
},
"className": "dt-center"
},
{
"data": "case_name",
"className": "dt-center",
"render": function (data, type, row) {
return `<a target="_blank" rel="noopener" href="/case?cid=${row.case_id}">${sanitizeHTML(data)}</a>`;
}
},
{
"data": "access_level_list",
"render": function ( data, type, row ) {
ret_data = "";
for (acc in data) {
ret_data += `<span class="badge ml-2 badge-light">${data[acc].name}</span>`
}
return ret_data;
},
"className": "dt-center"
}
],
filter: true,
info: true,
ordering: true,
processing: true,
select: true
});
var actionOptionsUser = {
classes: [],
contextMenu: {
enabled: true,
isMulti: true,
xoffset: -10,
yoffset: -10,
headerRenderer: function (rows) {
if (rows.length > 1) {
return rows.length + ' items selected';
} else {
let row = rows[0];
return 'Quick action';
}
},
},
buttonList: {
enabled: false,
},
deselectAfterAction: true,
items: [],
};
actionOptionsUser.items.push({
type: 'option',
title: 'Remove access',
multi: true,
iconClass: 'fas fa-trash',
buttonClasses: ['btn', 'btn-outline-primary'],
action: function(rows){
remove_cases_access_from_user_table('{{ user.user_id }}', rows);
}
});
modal_user_cac_table.contextualActions(actionOptionsUser);
{% if user.user_id %}
current_user_cases_access_list = {{ user.user_cases_access|tojson }};
modal_user_cac_table.rows.add(current_user_cases_access_list);
modal_user_cac_table.columns.adjust().draw();
{% endif %}
</script>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->

View File

@ -0,0 +1,66 @@
<div class="modal-lg modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title mt-2 mr-4">Set case access</h4>
<div class="row text-right">
<button type="button" class="pull-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
</div>
<div class="modal-body">
<div class="container col-md-12" >
<div class="row">
<div class="form-group" data-select2-id="7">
<label>Set cases access of user <i>{{ user.user_name }}</i> *</label>
<div class="col-12">
<select id="user_case_access_select" name="org_case_access_select" class="form-control ml-12" multiple="multiple"
tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
<select id="user_case_ac_select" name="org_case_ac_select" class="form-control ml-12"
tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
</div>
</div>
</div>
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right"
id="grant_case_access_to_user">Set access</button>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
var data = [];
$('#user_case_access_select').multiselect({
buttonWidth: 400,
nonSelectedText: 'Select case',
emptyText: 'No case available to add',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#user_case_access_select').multiselect('dataprovider', [{% for ocs in outer_cases %}
{ label: "{{ ocs.case_name }}", value: {{ ocs.case_id }} }, {% endfor %}]);
$('#user_case_access_select').multiselect('refresh')
$('#user_case_ac_select').multiselect({
nonSelectedText: 'Select access level',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#user_case_ac_select').multiselect('dataprovider', [{% for acc in access_levels %}
{ label: "{{ acc.name }}", value: {{ acc.value }} }, {% endfor %}]);
$('#user_case_ac_select').multiselect('refresh');
</script>

View File

@ -0,0 +1,68 @@
<div class="modal-lg modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title mt-2 mr-4">Set case access via groups</h4>
<div class="row text-right">
<button type="button" class="pull-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
</div>
<div class="modal-body">
<div class="container col-md-12" >
<div class="row">
<div class="form-group" data-select2-id="7">
<label>Set groups case access</label>
<div class="row">
<div class="col-12">
<select id="group_case_access_select" name="group_case_access_select" class="form-control select2-hidden-accessible ml-12" multiple="multiple"
tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
<select id="group_case_ac_select" name="group_case_ac_select" class="form-control select2-hidden-accessible ml-12"
tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
</div>
</div>
</div>
</div>
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right"
onclick="set_case_access_via_group('{{ caseid }}')">Set access</button>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
var data = [];
$('#group_case_access_select').multiselect({
buttonWidth: 400,
nonSelectedText: 'Select group(s)',
emptyText: 'No groups available to set',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#group_case_access_select').multiselect('dataprovider', [{% for ocs in groups %}
{ label: "{{ ocs.group_name }}", value: {{ ocs.group_id }} }, {% endfor %}]);
$('#group_case_access_select').multiselect('refresh')
$('#group_case_ac_select').multiselect({
nonSelectedText: 'Select access level',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#group_case_ac_select').multiselect('dataprovider', [{% for acc in access_levels %}
{ label: "{{ acc.name }}", value: {{ acc.value }} }, {% endfor %}]);
$('#group_case_ac_select').multiselect('refresh');
</script>

View File

@ -0,0 +1,65 @@
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-heading">
<div class="card-title">Case access</div>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
<p>Click on a user to unveil access control path</p>
<table class="table table-striped table-bordered" id="case_audit_access_table">
<thead>
<tr>
<th>User ID</th>
<th>User Name</th>
<th>User UUID</th>
<th>Access</th>
</tr>
</thead>
<tbody>
{% for user_id in access_audit %}
<tr>
<td>
{% if access_audit[user_id].user_effective_access_list|length == 0 %}
<i class="mr-2 fa-solid text-danger fa-circle-xmark" title="No access"></i>
{% elif access_audit[user_id].user_effective_access_list|length == 1 %}
<i class="mr-2 fa-solid text-warning fa-circle-minus" title="Partial access"></i>
{% else %}
<i class="mr-2 fa-solid fa-circle-check text-success" title="All access"></i>
{% endif %}
{{ user_id }}
</td>
<td>{{ access_audit[user_id].user_info.user_name }}</td>
<td>{{ access_audit[user_id].user_info.user_uuid }}</td>
<td>{% if access_audit[user_id].user_effective_access_list|length == 0 %}
<span class="badge badge-pill badge-light" title="Click to show trace" href="#collapse_cac_{{user_id}}" style="cursor:pointer;" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapse_cac_{{user_id}}">No access</span>
{% else %}
{% for uac in access_audit[user_id].user_effective_access_list %}
<span class="badge badge-pill badge-light" title="Click to show trace" href="#collapse_cac_{{user_id}}" style="cursor:pointer;" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapse_cac_{{user_id}}">{{ uac }}</span>
{% endfor %}
<i class="fa-solid fa-eye ml-2" title="Click to show trace" href="#collapse_cac_{{user_id}}" style="cursor:pointer;" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapse_cac_{{user_id}}"></i>
{% endif %}
<ol class="activity-feed collapse" id="collapse_cac_{{user_id}}">
{% if access_audit[user_id].user_effective_access_list|length == 0 %}
The user is neither in a group nor an organisation who has access to this case
{% endif %}
{% for uac in access_audit[user_id].access_trace %}
<li class="feed-item {% if uac.state == 'Effective' %} feed-item-success {% else %} feed-item-danger {% endif %}" title="{{ uac.state }}">
<span class="text"><span class="badge badge-pill badge-light">{{ uac.name }}</span> ({{ uac.state }})</span><br />
<span class="text">Inherited from {{ uac.inherited_from.object_type }} <i class="fa-solid fa-right-long"></i> {{ uac.inherited_from.object_name }} (ID {{ uac.inherited_from.object_id }} :: UUID {{ uac.inherited_from.object_uuid }})</span>
</li>
{% endfor %}
</ol>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,62 @@
<div class="modal-lg modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title mt-2 mr-4">Manage user groups</h4>
<div class="row text-right">
<button type="button" class="pull-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
</div>
<div class="modal-body">
<div class="container col-md-12" >
<div class="row">
<div class="form-group" data-select2-id="7">
<label>Groups membership *</label>
<div class="select2-input ml-12" data-select2-id="6">
<select id="user_groups_membership" name="user_groups_membership" class="form-control select2-hidden-accessible ml-12" multiple=""
tabindex="-1" aria-hidden="true" style="width: 100%">
</select>
</div>
</div>
</div>
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right"
id="save_user_groups_membership">Save</button>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
var data = [];
if (current_groups_list.length === 0) {
refresh_groups();
}
for (group in current_groups_list) {
data.push({
label: `${current_groups_list[group].group_name}`,
value: current_groups_list[group].group_id
});
}
$('#user_groups_membership').multiselect({
buttonWidth: 400,
nonSelectedText: 'Select groups',
includeSelectAllOption: true,
enableFiltering: true,
enableCaseInsensitiveFiltering: true,
filterPlaceholder: 'Search',
filterBehavior: 'both',
widthSynchronizationMode: 'ifPopupIsSmaller'
});
$('#user_groups_membership').multiselect('dataprovider', data );
{% if user.user_groups %}
$('#user_groups_membership').multiselect('select', [
{% for group in user.user_groups %} {{ group.group_id }}, {% endfor %}
]);
$('#user_groups_membership').multiselect('refresh')
{% endif %}
</script>

View File

@ -0,0 +1,94 @@
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-heading">
<div class="card-title">User permissions</div>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
<p class="mb-4">Permissions are inherited from the groups the user belongs to. The table shows the effective permissions the user has on the platform.</p>
<table class="table table-striped table-bordered" id="user_audit_permissions_table">
<thead>
<tr>
<th>Permission</th>
<th>Value</th>
<th>Inherited from groups</th>
</tr>
</thead>
<tbody>
{% for perm in permissions_audit['details'] %}
<tr>
<td>{{ permissions_audit['details'][perm].name }}</td>
<td>0x{{ perm | int(perm,16) }}</td>
<td>{% for group_id in permissions_audit['details'][perm].inherited_from %}<span class="badge ml-2 badge-light">{{ permissions_audit['details'][perm].inherited_from[group_id].group_name }}</span>{% endfor %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-heading">
<div class="card-title">User cases access</div>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
<p>Click on a case to unveil access control path</p>
<table class="table table-striped table-bordered" id="user_audit_access_table">
<thead>
<tr>
<th>Case name</th>
<th>Access</th>
</tr>
</thead>
<tbody>
{% for case_id in access_audit %}
<tr>
<td>
{% if access_audit[case_id].user_effective_access|length == 0 or 'deny_all' in access_audit[case_id].user_effective_access[0].name %}
<i class="mr-2 fa-solid text-danger fa-eye-slash" title="No access"></i>
{% elif 'read_only' in access_audit[case_id].user_effective_access[0].name %}
<i class="mr-2 fa-solid text-warning fa-lock" title="Read only access"></i>
{% else %}
<i class="mr-2 fa-solid fa-circle-check text-success" title="Full access"></i>
{% endif %}
{{ access_audit[case_id].case_info.case_name }}
</td>
<td>{% if access_audit[case_id].user_effective_access|length == 0 %}
<span class="badge badge-pill badge-light" title="Click to show trace" href="#collapse_cac_{{case_id}}" style="cursor:pointer;" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapse_cac_{{case_id}}">No access</span>
{% else %}
{% for uac in access_audit[case_id].user_effective_access %}
<span class="badge badge-pill badge-light" title="Click to show trace" href="#collapse_cac_{{case_id}}" style="cursor:pointer;" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapse_cac_{{case_id}}">{{ uac.name }}</span>
{% endfor %}
<i class="fa-solid fa-eye ml-2" title="Click to show trace" href="#collapse_cac_{{case_id}}" style="cursor:pointer;" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapse_cac_{{case_id}}"></i>
{% endif %}
<ol class="activity-feed collapse" id="collapse_cac_{{case_id}}">
{% if access_audit[case_id].user_effective_access|length == 0 %}
The user is neither in a group nor an organisation who has access to this case
{% endif %}
{% for uac in access_audit[case_id].user_access %}
<li class="feed-item {% if uac.state == 'Effective' %} feed-item-success {% else %} feed-item-danger {% endif %}" title="{{ uac.state }}">
<span class="text">{% for ac in uac.access_list %}<span class="badge badge-pill badge-light">{{ ac.name }}</span>{% endfor %} ({{ uac.state }})</span><br />
<span class="text">Inherited from {{ uac.inherited_from.object_type }} <i class="fa-solid fa-right-long"></i> {{ uac.inherited_from.object_name }} (ID {{ uac.inherited_from.object_id }} :: UUID {{ uac.inherited_from.object_uuid }})</span>
</li>
{% endfor %}
</ol>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>