Bug 37266: patron_lists/delete.pl should have CSRF protection

This patch adds CSRF protection to patron list deletions.

Also changed: The "Delete selected lists" button is now in a floating
toolbar.

To test, apply the patch and go to Tools -> Patron lists.

- If necessary, create a few patron lists.
- Test the two methods for list deletion available on the page:
  - Check one or more checkboxes and then click the "Delete selected
    lists" at the top of the page.
  - Click the "Actions" button for an individual list and choose "Delete
    list."
- Open the checkout page for a patron.
  - Under the "Patron lists" tab, add the patron to a list.
  - Click the "Actions" button for an that list and choose "Delete
    list."
  - When you are taken to the patron lists page the list should have
    been deleted.
- Perform the same test on the patron details page.

Sponsored-by: Athens County Public Libraries
Signed-off-by: Phil Ringnalda <phil@chetcolibrary.org>
Signed-off-by: Julian Maurice <julian.maurice@biblibre.com>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
This commit is contained in:
Owen Leonard 2025-01-24 17:38:00 +00:00 committed by Katrin Fischer
parent b504d8c5e9
commit 08a9ded6b0
Signed by: kfischer
GPG key ID: 0EF6E2C03357A834
5 changed files with 142 additions and 102 deletions

View file

@ -1199,6 +1199,7 @@
[% Asset.js("js/checkouts.js") | $raw %]
[% Asset.js("js/tables/bookings.js") | $raw %]
[% Asset.js("js/recalls.js") | $raw %]
[% Asset.js("js/form-submit.js") | $raw %]
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]

View file

@ -786,6 +786,7 @@
[% INCLUDE 'str/members-menu.inc' %]
[% Asset.js("js/members-menu.js") | $raw %]
[% Asset.js("js/recalls.js") | $raw %]
[% Asset.js("js/form-submit.js") | $raw %]
<script>
const LoadCheckoutsTableDelay = 0;
const AlwaysLoadCheckoutsTable = [% Koha.Preference('AlwaysLoadCheckoutsTable') | html %];

View file

@ -40,11 +40,20 @@
<h1>Patron lists</h1>
[% IF ( lists ) %]
<form action="/cgi-bin/koha/patron_lists/delete.pl" method="post" id="patrons_lists_form">
[% INCLUDE 'csrf-token.inc' %]
<input type="hidden" name="op" value="cud-delete" />
<div id="searchheader" class="searchheader noprint sticky">
<div class="btn-group">
<button type="submit" class="btn btn-default btn-sm disabled" id="delete_selected_lists"><i class="fa fa-trash" aria-hidden="true"></i> Delete selected lists</button>
</div>
</div>
<div class="page-section">
<table id="patron-lists-table">
<thead>
<tr>
<input type="button" type="submit" class="btn btn-default btn-sm disabled" value="Delete selected lists" id="delete_selected_lists" />
<th class="NoSort"></th>
<th>Name</th>
<th>Patrons in list</th>
<th>Shared</th>
@ -57,7 +66,9 @@
[% SET shared_by_other = l.owner.id != logged_in_user.id %]
<tr>
<td>
<input class="select_patron" type="checkbox" autocomplete="off" data-patron-list-id="[% l.patron_list_id | html %]" />
<input class="select_list" name="patron_lists_ids" value="[% l.patron_list_id | html %]" type="checkbox" autocomplete="off" data-patron-list-id="[% l.patron_list_id | html %]" />
</td>
<td>
<a href="/cgi-bin/koha/patron_lists/list.pl?patron_list_id=[% l.patron_list_id | uri %]">[% l.name | html %]</a>
</td>
<td>[% l.patron_list_patrons_rs.count || 0 | html %]</td>
@ -79,13 +90,22 @@
>
[% UNLESS shared_by_other %]
<li
><a class="dropdown-item" href="/cgi-bin/koha/patron_lists/add-modify.pl?patron_list_id=[% l.patron_list_id | uri %]"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit list</a></li
>
<li
><a class="delete_patron dropdown-item" href="/cgi-bin/koha/patron_lists/delete.pl?patron_list_id=[% l.patron_list_id | html %]" data-list-name="[% l.name | html %]"
><i class="fa fa-trash-can"></i> Delete list</a
><a class="dropdown-item" href="/cgi-bin/koha/patron_lists/add-modify.pl?patron_list_id=[% l.patron_list_id | uri %]"
><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit list</a
></li
>
<li>
<a
class="dropdown-item submit-form-link"
href="#"
data-patron_list_id="[% l.patron_list_id | html %]"
data-action="delete.pl"
data-method="post"
data-op="cud-delete"
data-confirmation-msg="Are you sure you want to delete this list?"
><i class="fa fa-trash-can"></i> Delete list</a
>
</li>
[% END %]
[% IF ( l.patron_list_patrons_rs.count ) %]
<li><hr class="dropdown-divider" /></li>
@ -118,6 +138,8 @@
</table>
</div>
<!-- /.page-section -->
</form>
<!-- /#patron_lists_form -->
<!-- Modal to print patron cards -->
<div class="modal" id="patronExportModal" tabindex="-1" role="dialog" aria-labelledby="patronExportModal_label" aria-hidden="true">
@ -140,6 +162,7 @@
[% MACRO jsinclude BLOCK %]
[% Asset.js("js/tools-menu.js") | $raw %]
[% Asset.js("js/form-submit.js") | $raw %]
[% INCLUDE 'datatables.inc' %]
<script>
$(document).ready(function() {
@ -152,32 +175,34 @@
autoWidth: false,
columnDefs: [{ orderable: false, searchable: false, targets: ["NoSort"] }],
pagingType: "full",
"sorting": [[ 1, "asc" ]]
});
$(".delete_patron").on("click", function(){
$(".dropdown").removeClass("open");
var list = $(this).data("list-name");
return confirmDelete( _("Are you sure you want to delete the list %s?").format(list));
});
$("#delete_selected_lists").on("click", function() {
if (selectedPatronLists.length != 0) {
if (confirm(_("Are you sure you want to delete the selected lists ?"))) {
var delete_lists_url = '/cgi-bin/koha/patron_lists/delete.pl?patron_lists_ids=' + selectedPatronLists.join("&patron_lists_ids=");
window.location.href = delete_lists_url;
$("#patrons_lists_form").submit(function(){
var checkedItems = $("input[name=patron_lists_ids]:checked");
if ( checkedItems.size() == 0) {
alert(_("You must select one or more lists to delete"));
return false;
}
if( confirm(_("Are you sure you want to delete the selected lists?")) ) {
return true;
} else {
return false;
}
});
$(document).on("click", ".select_patron", function() {
if($(this).is(':checked')){
$("#delete_selected_lists").attr("class","btn btn-default btn-sm");
selectedPatronLists.push($(this).data("patron-list-id"));
}
else {
selectedPatronLists = selectedPatronLists.filter(item => item !== $(this).data("patron-list-id"));
if(selectedPatronLists.length === 0){
$("#delete_selected_lists").attr("class","btn btn-default btn-sm disabled");
}
$(document).on("click", ".select_list", function() {
var checkedItems = $("input[name=patron_lists_ids]:checked");
if ( checkedItems.size() == 0 ) {
$("#delete_selected_lists").addClass("disabled").prop("disabled", true);
} else {
$("#delete_selected_lists").removeClass("disabled").prop("disabled", false);
}
});

View file

@ -1,3 +1,6 @@
[% USE raw %]
[% USE Koha %]
[% USE Asset %]
[% USE KohaDates %]
[% IF no_access_to_patron %]
@ -57,11 +60,18 @@
<li
><a class="dropdown-item" href="/cgi-bin/koha/patron_lists/add-modify.pl?patron_list_id=[% l.patron_list_id | uri %]"><i class="fa fa-pencil"></i> Edit list</a></li
>
<li
><a class="delete_patron dropdown-item" href="/cgi-bin/koha/patron_lists/delete.pl?patron_list_id=[% l.patron_list_id | html %]" data-list-name="[% l.name | html %]"
><i class="fa fa-trash"></i> Delete list</a
></li
<li>
<a
class="dropdown-item submit-form-link"
href="#"
data-patron_list_id="[% l.patron_list_id | html %]"
data-action="/cgi-bin/koha/patron_lists/delete.pl"
data-method="post"
data-op="cud-delete"
data-confirmation-msg="Are you sure you want to delete this list?"
><i class="fa fa-trash-can"></i> Delete list</a
>
</li>
[% END %]
[% IF ( l.patron_list_patrons_rs.count ) %]
<li><hr class="dropdown-divider" /></li>

View file

@ -26,6 +26,7 @@ use C4::Output;
use Koha::List::Patron qw( DelPatronList );
my $cgi = CGI->new;
my $op = $cgi->param('op') // q{};
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
{
@ -39,13 +40,15 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
my $id = $cgi->param('patron_list_id');
my @lists_ids = $cgi->multi_param('patron_lists_ids');
if ( defined $id && $id ne '' ) {
if ( $op eq 'cud-delete' ) {
if ( defined $id && $id ne '' ) {
DelPatronList( { patron_list_id => $id } );
}
if (@lists_ids) {
}
if (@lists_ids) {
foreach my $list_id (@lists_ids) {
DelPatronList( { patron_list_id => $list_id } );
}
}
}
print $cgi->redirect('lists.pl');