218 lines
6.7 KiB
Perl
218 lines
6.7 KiB
Perl
package Koha::CookieManager;
|
|
|
|
# Copyright 2022 Rijksmuseum, Koha Development Team
|
|
#
|
|
# This file is part of Koha.
|
|
#
|
|
# Koha is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Koha is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
|
|
|
use Modern::Perl;
|
|
use CGI::Cookie;
|
|
|
|
# use Data::Dumper qw( Dumper );
|
|
# use List::MoreUtils qw( uniq );
|
|
|
|
use C4::Context;
|
|
|
|
# The cookies on the following list are removed unless koha-conf.xml indicates otherwise.
|
|
# koha-conf.xml may also contain additional cookies to be removed.
|
|
# Not including here: always_show_holds, catalogue_editor_, issues-table-load-immediately-circulation,
|
|
# and ItemEditorSessionTemplateId.
|
|
# TODO Not sure about branch (rotatingcollections), patronSessionConfirmation (circulation.pl)
|
|
use constant MANAGED_COOKIE_PREFIXES => qw{
|
|
bib_list
|
|
CGISESSID
|
|
holdfor holdforclub
|
|
intranet_bib_list
|
|
JWT
|
|
KohaOpacLanguage
|
|
LastCreatedItem
|
|
marctagstructure_selectdisplay
|
|
search_path_code
|
|
searchToOrder
|
|
};
|
|
use constant PATH_EXCEPTIONS => {
|
|
always_show_holds => '/cgi-bin/koha/reserve',
|
|
};
|
|
use constant KEEP_COOKIE_CONF_VAR => 'do_not_remove_cookie';
|
|
use constant REMOVE_COOKIE_CONF_VAR => 'remove_cookie';
|
|
|
|
our $cookies;
|
|
|
|
=head1 NAME
|
|
|
|
Koha::CookieManager - Object for unified handling of cookies in Koha
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Koha::CookieManager;
|
|
my $mgr = Koha::CookieManager->new;
|
|
|
|
# Replace cookies
|
|
$cookie_list = $mgr->replace_in_list( [ $cookie1, $cookie2_old ], $cookie2_new );
|
|
|
|
# Clear cookies
|
|
$cookie_list = $mgr->clear_unless( $cookie1, $cookie2, $cookie3_name );
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
The current object allows you to remove cookies on the hardcoded list
|
|
in this module, refined by 'keep' or 'remove' entries in koha-conf.xml.
|
|
Note that a keep entry overrules a remove.
|
|
|
|
This module also offers a method to replace the old version of a cookie
|
|
by a new one.
|
|
|
|
The module could be extended by (gradually) routing cookie creation
|
|
through it in order to consistently fill cookie parameters like httponly,
|
|
secure and samesite flag, etc. And could serve to register all our cookies
|
|
in a central location.
|
|
|
|
=head1 METHODS
|
|
|
|
=head2 new
|
|
|
|
my $mgr = Koha::CookieManager->new({}); # parameters for extensions
|
|
|
|
=cut
|
|
|
|
sub new {
|
|
my ( $class, $params ) = @_;
|
|
my $self = bless $params // {}, $class;
|
|
|
|
# Get keep and remove list from koha-conf (scalar or arrayref)
|
|
my $keep_list = C4::Context->config(KEEP_COOKIE_CONF_VAR) || [];
|
|
$self->{_keep_list} = ref($keep_list) ? $keep_list : [$keep_list];
|
|
my $remove_list = C4::Context->config(REMOVE_COOKIE_CONF_VAR) || [];
|
|
$self->{_remove_list} = ref($remove_list) ? $remove_list : [$remove_list];
|
|
|
|
$self->{_secure} = C4::Context->https_enabled;
|
|
return $self;
|
|
}
|
|
|
|
=head2 clear_unless
|
|
|
|
$cookies = $self->clear_unless( $query->cookie, @$cookies );
|
|
|
|
Arguments: either cookie names or cookie objects (CGI::Cookie).
|
|
Note: in the example above $query->cookie is a list of cookie names as returned
|
|
by the CGI object.
|
|
|
|
Returns an arrayref of cookie objects: empty, expired cookies for
|
|
cookies on the remove list, together with the remaining (untouched)
|
|
cookie objects.
|
|
|
|
=cut
|
|
|
|
sub clear_unless {
|
|
my ( $self, @cookies ) = @_;
|
|
my @rv;
|
|
my $seen = {};
|
|
foreach my $c (@cookies) {
|
|
my $name;
|
|
my $type = ref($c);
|
|
if ( $type eq 'CGI::Cookie' ) {
|
|
$name = $c->name;
|
|
} elsif ($type) { # not expected: ignore
|
|
next;
|
|
} else {
|
|
$name = $c;
|
|
}
|
|
next if !$name;
|
|
|
|
if ( $self->_should_be_removed($name) ) {
|
|
next if $seen->{$name};
|
|
$seen->{$name} = 1; # prevent duplicates
|
|
if ($type) {
|
|
$c->max_age(0);
|
|
push @rv, _correct_path($c);
|
|
} else {
|
|
push @rv, _correct_path( CGI::Cookie->new( -name => $name, -value => q{}, '-max-age' => 0 ) );
|
|
}
|
|
} elsif ( $type eq 'CGI::Cookie' ) { # keep the last occurrence
|
|
@rv = @{ $self->replace_in_list( \@rv, $c ) };
|
|
}
|
|
}
|
|
return \@rv;
|
|
}
|
|
|
|
=head2 replace_in_list
|
|
|
|
$list2 = $mgr->replace_in_list( $list1, $cookie );
|
|
|
|
Add $cookie to $list1, removing older occurrences in list1.
|
|
$list1 is a list of CGI::Cookie objects.
|
|
$cookie must be a CGI::Cookie object; if it is not, only
|
|
cookie objects in list1 are returned (filtering list1).
|
|
|
|
Returns an arrayref of CGI::Cookie objects.
|
|
|
|
=cut
|
|
|
|
sub replace_in_list {
|
|
my ( $self, $list, $cookie ) = @_;
|
|
my $name = ref($cookie) eq 'CGI::Cookie' ? $cookie->name : q{};
|
|
|
|
my @result;
|
|
foreach my $c (@$list) {
|
|
next if ref($c) ne 'CGI::Cookie';
|
|
push @result, $c if !$name or $c->name ne $name;
|
|
}
|
|
push @result, $cookie if $name;
|
|
return \@result;
|
|
}
|
|
|
|
# INTERNAL ROUTINES
|
|
|
|
sub _should_be_removed {
|
|
my ( $self, $name ) = @_;
|
|
|
|
# Is this a controlled cookie? Or is it added as 'keep' or 'remove' in koha-conf.xml?
|
|
# The conf entries are treated as prefix (no longer as regex).
|
|
return unless grep { $name =~ /^$_/ } MANAGED_COOKIE_PREFIXES(), @{ $self->{_remove_list} };
|
|
return !grep { $name =~ /^$_/ } @{ $self->{_keep_list} };
|
|
}
|
|
|
|
sub _correct_path {
|
|
my $cookie_object = shift;
|
|
my $path = PATH_EXCEPTIONS->{ $cookie_object->name } or return $cookie_object;
|
|
$cookie_object->path($path);
|
|
return $cookie_object;
|
|
}
|
|
|
|
=head1 ADDITIONAL COMMENTS
|
|
|
|
How do the keep or remove lines in koha-conf.xml work?
|
|
|
|
<do_not_remove_cookie>some_cookie</do_not_remove_cookie>
|
|
The name some_cookie should refer here to a cookie that is on the
|
|
hardcoded list in this module. If you do not want it to be cleared
|
|
(removed) on logout, include this line.
|
|
You might want to do this e.g. for KohaOpacLanguage.
|
|
|
|
<remove_cookie>another_cookie</remove_cookie>
|
|
The name another_cookie refers here to a cookie that is not on the
|
|
hardcoded list but you want this cookie to be cleared/removed on logout.
|
|
It could be a custom cookie.
|
|
|
|
Note that both directives use the cookie name as a prefix. So if you
|
|
add a remove line for cookie1, it also affects cookie12, etc.
|
|
Since a keep line overrules a remove line, this allows you to add
|
|
lines for removing cookie1 and not removing cookie12 in order to
|
|
remove cookie1, cookie11, cookie13 but not cookie12, etc.
|
|
|
|
=cut
|
|
|
|
1;
|