From 5cb032eebd279b2d9d82d03f42a860c8581962ec Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Thu, 31 Aug 2017 10:48:47 -0400 Subject: [PATCH] Bug 20813: Revamp user permissions system Koha's current user permissions system is a bit convoluted and limited due to it's legacy where the permissions system was a simple set of flags. A second layer of sub-permissions were later added complication and obfuscation to the system. We should revamp the permissions system to be a more modern system and open the path to having unlimited nesting of sub-permissions instead of shoe-horning sub-permissions into the limited system we have. Test Plan: 1) Apply this patch set 2) Run updatedatabase.pl 3) Restart all the things! 4) Koha should still enforce user permissions as before --- C4/Acquisition.pm | 2 +- C4/Auth.pm | 111 ++++------ C4/Budgets.pm | 3 +- Koha/Patron.pm | 50 +++++ Koha/Patron/Permission.pm | 52 +++++ Koha/Patron/Permissions.pm | 56 +++++ Koha/Permission.pm | 73 ++++++ Koha/Permissions.pm | 56 +++++ about.pl | 22 +- .../prog/en/includes/members-toolbar.inc | 2 +- .../intranet-tmpl/prog/en/includes/permissions.inc | 7 +- .../prog/en/modules/members/member-flags.tt | 167 ++++++++------ members/member-flags.pl | 246 ++++++++------------- 13 files changed, 530 insertions(+), 317 deletions(-) create mode 100644 Koha/Patron/Permission.pm create mode 100644 Koha/Patron/Permissions.pm create mode 100644 Koha/Permission.pm create mode 100644 Koha/Permissions.pm diff --git a/C4/Acquisition.pm b/C4/Acquisition.pm index a257017dc7..118fa7b3f3 100644 --- a/C4/Acquisition.pm +++ b/C4/Acquisition.pm @@ -848,7 +848,7 @@ sub CanUserManageBasket { my ($flags) = $sth->fetchrow_array; $sth->finish; - $userflags = C4::Auth::getuserflags($flags, $borrower->{userid}, $dbh); + $userflags = C4::Auth::getuserflags( { userid => $borrower->{userid} }); } unless ($userflags->{superlibrarian} diff --git a/C4/Auth.pm b/C4/Auth.pm index 7442b1dd78..64b1157000 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -36,6 +36,7 @@ use Koha::AuthUtils qw(get_script_name hash_password); use Koha::Library::Groups; use Koha::Libraries; use Koha::Patrons; +use Koha::Permissions; use POSIX qw/strftime/; use List::MoreUtils qw/ any /; use Encode qw( encode is_utf8); @@ -57,7 +58,6 @@ BEGIN { @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &checkpw_internal &checkpw_hash &get_all_subpermissions &get_user_subpermissions ); - %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] ); $ldap = C4::Context->config('useldapserver') || 0; $cas = C4::Context->preference('casAuthentication'); $shib = C4::Context->config('useshibboleth') || 0; @@ -1065,7 +1065,7 @@ sub checkauth { if ( $return == 1 ) { my $select = " - SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode, + SELECT borrowernumber, firstname, surname, borrowers.branchcode, branches.branchname as branchname, branches.branchprinter as branchprinter, email @@ -1088,10 +1088,10 @@ sub checkauth { } } if ( $sth->rows ) { - ( $borrowernumber, $firstname, $surname, $userflags, + ( $borrowernumber, $firstname, $surname, $branchcode, $branchname, $branchprinter, $emailaddress ) = $sth->fetchrow; $debug and print STDERR "AUTH_3 results: " . - "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n"; + "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$branchcode,$emailaddress\n"; } else { print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n"; } @@ -1143,7 +1143,6 @@ sub checkauth { $session->param( 'surname', $surname ); $session->param( 'branch', $branchcode ); $session->param( 'branchname', $branchname ); - $session->param( 'flags', $userflags ); $session->param( 'emailaddress', $emailaddress ); $session->param( 'ip', $session->remote_addr() ); $session->param( 'lasttime', time() ); @@ -1848,35 +1847,35 @@ sub checkpw_internal { my $sth = $dbh->prepare( - "select password,cardnumber,borrowernumber,userid,firstname,surname,borrowers.branchcode,branches.branchname,flags from borrowers join branches on borrowers.branchcode=branches.branchcode where userid=?" + "select password,cardnumber,borrowernumber,userid,firstname,surname,borrowers.branchcode,branches.branchname from borrowers join branches on borrowers.branchcode=branches.branchcode where userid=?" ); $sth->execute($userid); if ( $sth->rows ) { my ( $stored_hash, $cardnumber, $borrowernumber, $userid, $firstname, - $surname, $branchcode, $branchname, $flags ) + $surname, $branchcode, $branchname ) = $sth->fetchrow; if ( checkpw_hash( $password, $stored_hash ) ) { C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber, - $firstname, $surname, $branchcode, $branchname, $flags ) unless $no_set_userenv; + $firstname, $surname, $branchcode, $branchname ) unless $no_set_userenv; return 1, $cardnumber, $userid; } } $sth = $dbh->prepare( - "select password,cardnumber,borrowernumber,userid,firstname,surname,borrowers.branchcode,branches.branchname,flags from borrowers join branches on borrowers.branchcode=branches.branchcode where cardnumber=?" + "select password,cardnumber,borrowernumber,userid,firstname,surname,borrowers.branchcode,branches.branchname from borrowers join branches on borrowers.branchcode=branches.branchcode where cardnumber=?" ); $sth->execute($userid); if ( $sth->rows ) { my ( $stored_hash, $cardnumber, $borrowernumber, $userid, $firstname, - $surname, $branchcode, $branchname, $flags ) + $surname, $branchcode, $branchname ) = $sth->fetchrow; if ( checkpw_hash( $password, $stored_hash ) ) { C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber, - $firstname, $surname, $branchcode, $branchname, $flags ) unless $no_set_userenv; + $firstname, $surname, $branchcode, $branchname ) unless $no_set_userenv; return 1, $cardnumber, $userid; } } @@ -1900,47 +1899,39 @@ sub checkpw_hash { =head2 getuserflags - my $authflags = getuserflags($flags, $userid, [$dbh]); + my $authflags = getuserflags($userid); Translates integer flags into permissions strings hash. -C<$flags> is the integer userflags value ( borrowers.userflags ) C<$userid> is the members.userid, used for building subpermissions -C<$authflags> is a hashref of permissions =cut sub getuserflags { - my $flags = shift; - my $userid = shift; - my $dbh = @_ ? shift : C4::Context->dbh; + my $params = shift; + + my $userid = $params->{userid}; + my $force_subpermissions = $params->{force_subpermissions}; + + my $patron = Koha::Patrons->find( { userid => $userid } ); + return unless $patron; + my $userflags; - { - # I don't want to do this, but if someone logs in as the database - # user, it would be preferable not to spam them to death with - # numeric warnings. So, we make $flags numeric. - no warnings 'numeric'; - $flags += 0; - } - my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags"); - $sth->execute; + my @permissions = Koha::Permissions->search( { parent => undef } ); + foreach my $p (@permissions) { + my $patron_has = $p->patron_has( $patron->id ); - while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) { - if ( ( $flags & ( 2**$bit ) ) || $defaulton ) { - $userflags->{$flag} = 1; + if ( $patron_has && !$force_subpermissions ) { + $userflags->{ $p->code() } = $p->patron_has( $patron->id ); } else { - $userflags->{$flag} = 0; + my @subpermissions = $p->subpermissions; + foreach my $sp ( $p->subpermissions ) { + $userflags->{ $p->code } = $sp->patron_has( $patron->id ); + } } } - # get subpermissions and merge with top-level permissions - my $user_subperms = get_user_subpermissions($userid); - foreach my $module ( keys %$user_subperms ) { - next if $userflags->{$module} == 1; # user already has permission for everything in this module - $userflags->{$module} = $user_subperms->{$module}; - } - return $userflags; } @@ -1972,20 +1963,7 @@ necessary to check borrowers.flags. sub get_user_subpermissions { my $userid = shift; - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare( "SELECT flag, user_permissions.code - FROM user_permissions - JOIN permissions USING (module_bit, code) - JOIN userflags ON (module_bit = bit) - JOIN borrowers USING (borrowernumber) - WHERE userid = ?" ); - $sth->execute($userid); - - my $user_perms = {}; - while ( my $perm = $sth->fetchrow_hashref ) { - $user_perms->{ $perm->{'flag'} }->{ $perm->{'code'} } = 1; - } - return $user_perms; + return getuserflags( { userid => $userid, force_subpermissions => 1 } ); } =head2 get_all_subpermissions @@ -1993,25 +1971,22 @@ sub get_user_subpermissions { my $perm_hashref = get_all_subpermissions(); Returns a hashref of hashrefs defining all specific -permissions currently defined. The return value -has the same structure as that of C, -except that the innermost hash value is the description -of the subpermission. +permissions currently defined. The return value has +the same basic structure as that of C =cut sub get_all_subpermissions { - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare( "SELECT flag, code - FROM permissions - JOIN userflags ON (module_bit = bit)" ); - $sth->execute(); - - my $all_perms = {}; - while ( my $perm = $sth->fetchrow_hashref ) { - $all_perms->{ $perm->{'flag'} }->{ $perm->{'code'} } = 1; + my $permissions; + + my @permissions = Koha::Permissions->search({ parent => undef }); + foreach my $p ( @permissions ) { + foreach my $sp ( $p->subpermissions ) { + $permissions->{ $p->code }->{ $sp->code } = 1; + } } - return $all_perms; + + return $permissions; } =head2 haspermission @@ -2027,10 +2002,8 @@ Returns member's flags or 0 if a permission is not met. sub haspermission { my ( $userid, $flagsrequired ) = @_; - my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?"); - $sth->execute($userid); - my $row = $sth->fetchrow(); - my $flags = getuserflags( $row, $userid ); + + my $flags = getuserflags( { userid => $userid } ); if ( $userid eq C4::Context->config('user') ) { # Super User Account from /etc/koha.conf diff --git a/C4/Budgets.pm b/C4/Budgets.pm index 644c5e59c9..c9c87de0b9 100644 --- a/C4/Budgets.pm +++ b/C4/Budgets.pm @@ -971,8 +971,7 @@ sub CanUserUseBudget { return 0 unless ($borrower and $budget); if (not defined $userflags) { - $userflags = C4::Auth::getuserflags($borrower->{flags}, - $borrower->{userid}); + $userflags = C4::Auth::getuserflags( { userid => $borrower->{userid} } ); } unless ($userflags->{superlibrarian} diff --git a/Koha/Patron.pm b/Koha/Patron.pm index 35cfce4ca1..8fd2425edd 100644 --- a/Koha/Patron.pm +++ b/Koha/Patron.pm @@ -34,6 +34,7 @@ use Koha::Old::Checkouts; use Koha::Patron::Categories; use Koha::Patron::HouseboundProfile; use Koha::Patron::HouseboundRole; +use Koha::Patron::Permissions; use Koha::Patron::Images; use Koha::Patrons; use Koha::Virtualshelves; @@ -1034,6 +1035,55 @@ sub generate_userid { =head2 Internal methods +=head3 has_permission + +my $bool = $patron->has_permission( $permission_code ); + +=cut + +sub has_permission { + my ( $self, $code ) = @_; + + return Koha::Patron::Permissions->find( + { + borrowernumber => $self->id, + code => $code, + } + ) ? 1 : 0; +} + +=head3 set_permissions + +$patron->set_permissions( { permissions => \@permissions } ); + +Given a list of permissions, set the patron to those and only those permissions + +=cut + +sub set_permissions { + my ( $self, $params ) = @_; + + my @permissions = @{ $params->{permissions} }; + + $self->_result->result_source->schema->txn_do( + sub { + Koha::Patron::Permissions->search( { borrowernumber => $self->id } ) + ->delete(); + + Koha::Patron::Permission->new( + { + borrowernumber => $self->id, + code => $_ + } + )->store() + for @permissions; + } + ); + + return $self; +} + + =head3 _type =cut diff --git a/Koha/Patron/Permission.pm b/Koha/Patron/Permission.pm new file mode 100644 index 0000000000..04ef51e9ff --- /dev/null +++ b/Koha/Patron/Permission.pm @@ -0,0 +1,52 @@ +package Koha::Patron::Permission; + +# Copyright ByWater Solutions 2017 +# +# 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Carp; + +use base qw(Koha::Object); + +=head1 NAME + +Koha::Patron::Permission - Koha User Permission Object class + +This class represents a single permission that a patron has + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub _type { + return 'UserPermission'; +} + +=head1 AUTHOR + +Kyle M Hall + +=cut + +1; diff --git a/Koha/Patron/Permissions.pm b/Koha/Patron/Permissions.pm new file mode 100644 index 0000000000..e3aaa0538d --- /dev/null +++ b/Koha/Patron/Permissions.pm @@ -0,0 +1,56 @@ +package Koha::Patron::Permissions; + +# Copyright ByWater Solutions 2017 +# +# 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Koha::Patron::Permission; + +use base qw(Koha::Objects); + +=head1 NAME + +Koha::Patron::Permissions - Koha Patron Permissions Object class + +This class represents a set of permissions a patron has + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub _type { + return 'UserPermission'; +} + +sub object_class { + return 'Koha::Patron::Permission'; +} + +=head1 AUTHOR + +Kyle M Hall + +=cut + +1; diff --git a/Koha/Permission.pm b/Koha/Permission.pm new file mode 100644 index 0000000000..798702cea1 --- /dev/null +++ b/Koha/Permission.pm @@ -0,0 +1,73 @@ +package Koha::Permission; + +# Copyright ByWater Solutions 2017 +# +# 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Carp; + +use Koha::Patron::Permissions; + +use base qw(Koha::Object); + +=head1 NAME + +Koha::Permission - Koha Permission Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 patron_has + + Accepts a patron id, returns true if patron has this permission, false otherwise +=cut + +sub patron_has { + my ( $self, $patron ) = @_; + + return Koha::Patron::Permissions->find( { borrowernumber => $patron, code => $self->code } ) ? 1 : 0; +} + +=head3 subpermissions + +=cut + +sub subpermissions { + my ( $self ) = @_; + + return Koha::Permissions->search( { parent => $self->code } ); +} + +=head3 type + +=cut + +sub _type { + return 'Permission'; +} + +=head1 AUTHOR + +Kyle M Hall + +=cut + +1; diff --git a/Koha/Permissions.pm b/Koha/Permissions.pm new file mode 100644 index 0000000000..2be4cfb324 --- /dev/null +++ b/Koha/Permissions.pm @@ -0,0 +1,56 @@ +package Koha::Permissions; + +# Copyright ByWater Solutions 2017 +# +# 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Koha::Permission; + +use base qw(Koha::Objects); + +=head1 NAME + +Koha::Permissions - Koha Permissions Object class + +This object represents a type of permission a user may have. + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub _type { + return 'Permission'; +} + +sub object_class { + return 'Koha::Permission'; +} + +=head1 AUTHOR + +Kyle M Hall + +=cut + +1; diff --git a/about.pl b/about.pl index 70900dd2c4..630fc7fbdb 100755 --- a/about.pl +++ b/about.pl @@ -43,6 +43,7 @@ use Koha::DateUtils qw(dt_from_string output_pref); use Koha::Acquisition::Currencies; use Koha::Patron::Categories; use Koha::Patrons; +use Koha::Patron::Permissions; use Koha::Caches; use Koha::Config::SysPrefs; use Koha::Illrequest::Config; @@ -414,21 +415,14 @@ if ( C4::Context->preference('WebBasedSelfCheck') and C4::Context->preference('AutoSelfCheckAllowed') ) { my $userid = C4::Context->preference('AutoSelfCheckID'); - my $all_permissions = C4::Auth::get_user_subpermissions( $userid ); - my ( $has_self_checkout_perm, $has_other_permissions ); - while ( my ( $module, $permissions ) = each %$all_permissions ) { - if ( $module eq 'self_check' ) { - while ( my ( $permission, $flag ) = each %$permissions ) { - if ( $permission eq 'self_checkout_module' ) { - $has_self_checkout_perm = 1; - } else { - $has_other_permissions = 1; - } - } - } else { - $has_other_permissions = 1; + my $patron = Koha::Patrons->find( { userid => $userid } ); + my $has_self_checkout_perm = $patron->has_permission('self_checkout'); + my $has_other_permissions = Koha::Patron::Permissions->search( + { + borrowernumber => $patron->id, + -not => { code => 'self_checkout' } } - } + )->count(); $template->param( AutoSelfCheckPatronDoesNotHaveSelfCheckPerm => not ( $has_self_checkout_perm ), AutoSelfCheckPatronHasTooManyPerm => $has_other_permissions, diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc index 9b5524d4c1..b1af18ead3 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc @@ -51,7 +51,7 @@
  • Renew patron
  • [% END %] [% IF ( CAN_user_permissions ) %] -
  • Set permissions
  • +
  • Set permissions
  • [% ELSE %]
  • Set permissions
  • [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc index 017ffb918e..dbd4c24707 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc @@ -1,4 +1,4 @@ -[%- BLOCK main_permissions -%] +[%- BLOCK permissions -%] [% SWITCH name %] [%- CASE 'superlibrarian' -%]Access to all librarian functions [%- CASE 'circulate' -%]Check out and check in items @@ -22,11 +22,6 @@ [%- CASE 'clubs' -%]Patron clubs [%- CASE 'ill' -%]Create and modify Interlibrary loan requests [%- CASE 'self_check' -%]Self check modules - [%- END -%] -[%- END -%] - -[%- BLOCK sub_permissions -%] - [% SWITCH name %] [%- CASE 'circulate_remaining_permissions' -%]Remaining circulation permissions [%- CASE 'force_checkout' -%]Force checkout if a limitation exists [%- CASE 'manage_restrictions' -%]Manage restrictions for accounts diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member-flags.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/member-flags.tt index 4d40e0acce..6c8c260675 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member-flags.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/member-flags.tt @@ -6,6 +6,43 @@ Koha › Patrons › Set permissions for [% patron.surname %], [% patron.firstname %] [% Asset.css("css/treeview/jquery.treeview.css") %] [% INCLUDE 'doc-head-close.inc' %] + + + @@ -15,74 +52,72 @@
    - -
    -
    -
    -[% INCLUDE 'members-toolbar.inc' %] - -
    - - - -

    Set permissions for [% patron.surname %], [% patron.firstname %]

    - -
      - - [% FOREACH loo IN loop %] - [% IF ( loo.expand ) %] -
    • - [% ELSE %] -
    • - [% END %] - [% IF ( loo.checked ) %] - [% IF disable_superlibrarian_privs && loo.bit == 0 %] - - - [% ELSE %] - - [% END %] - [% ELSE %] - [% IF disable_superlibrarian_privs && loo.bit == 0 %] - - [% ELSE %] - - [% END %] - [% END %] - - [% PROCESS main_permissions name=loo.flag %] - [% IF ( loo.sub_perm_loop ) %] -
        - [% FOREACH sub_perm_loo IN loo.sub_perm_loop %] -
      • - [% IF ( sub_perm_loo.checked ) %] - - [% ELSE %] - - [% END %] - - [% PROCESS sub_permissions name=sub_perm_loo.code %] -
      • +
        +
        +
        + [% INCLUDE 'members-toolbar.inc' %] + + + + + +

        Set permissions for [% surname %], [% firstname %]

        + + + + + + + [% FOREACH p IN permissions %] + [% NEXT IF p.code == 'superlibrarian' %] + + + + + + [% FOREACH s IN p.subpermissions() %] + + + + + [% END %] [% END %] - - - [% ELSE %] - - [% END %] - [% END %] - - - - -
        Cancel
        - - - - +
        + [% IF patron.has_permission( p.code ) %] + + [% ELSE %] + + [% END %] + superlibrarian[% PROCESS permissions name='superlibrarian' %] +
        + [% IF patron.has_permission( p.code ) %] + + [% ELSE %] + + [% END %] + [% p.code %][% PROCESS permissions name=p.code %] +
        + [% IF patron.has_permission( s.code ) %] + + [% ELSE %] + + [% END %] + [% s.code %][% PROCESS permissions name=s.code %] +
        -
        -[% INCLUDE 'circ-menu.inc' %] -
        + + +
        + + Cancel +
        + +
        +
        + +
        + [% INCLUDE 'circ-menu.inc' %] +
        [% MACRO jsinclude BLOCK %] diff --git a/members/member-flags.pl b/members/member-flags.pl index c527ff09f4..2786dcf0db 100755 --- a/members/member-flags.pl +++ b/members/member-flags.pl @@ -1,208 +1,138 @@ #!/usr/bin/perl -# script to edit a member's flags -# Written by Steve Tonnesen -# July 26, 2002 (my birthday!) +# Copyright 2017 ByWater Solutions; +# +# 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 . use Modern::Perl; use CGI qw ( -utf8 ); use C4::Output; -use C4::Auth qw(:DEFAULT :EditPermissions); +use C4::Auth; use C4::Context; use C4::Members; use C4::Members::Attributes qw(GetBorrowerAttributes); -#use C4::Acquisitions; -use Koha::Patron::Categories; use Koha::Patrons; +use Koha::Patron::Images; +use Koha::Patron::Categories; -use C4::Output; use Koha::Token; +use Koha::Permissions; my $input = new CGI; my $flagsrequired = { permissions => 1 }; -my $member=$input->param('member'); -my $patron = Koha::Patrons->find( $member ); -unless ( $patron ) { - print $input->redirect("/cgi-bin/koha/circ/circulation.pl?borrowernumber=$member"); - exit; -} +my $borrowernumber = $input->param('borrowernumber'); + +my $patron = Koha::Patrons->find($borrowernumber); my $category_type = $patron->category->category_type; -my $bor = $patron->unblessed; -if( $category_type eq 'S' ) { # FIXME Is this really needed? +my $bor = $patron->unblessed; +if ( $category_type eq 'S' ) { $flagsrequired->{'staffaccess'} = 1; } -my ($template, $loggedinuser, $cookie) = get_template_and_user({ +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { template_name => "members/member-flags.tt", query => $input, type => "intranet", authnotrequired => 0, flagsrequired => $flagsrequired, debug => 1, -}); + } +); my $logged_in_user = Koha::Patrons->find( $loggedinuser ) or die "Not logged in"; output_and_exit_if_error( $input, $cookie, $template, { module => 'members', logged_in_user => $logged_in_user, current_patron => $patron } ); -my %member2; -$member2{'borrowernumber'}=$member; - -if ($input->param('newflags')) { +if ( $input->param('set_permissions') ) { # update permissions die "Wrong CSRF token" - unless Koha::Token->new->check_csrf({ + unless Koha::Token->new->check_csrf( + { session_id => scalar $input->cookie('CGISESSID'), - token => scalar $input->param('csrf_token'), - }); + token => scalar $input->param('csrf_token'), + } + ); + my @permissions = $input->multi_param('permission'); - my $dbh=C4::Context->dbh(); + $patron->set_permissions( { permissions => \@permissions } ); - my @perms = $input->multi_param('flag'); - my %all_module_perms = (); - my %sub_perms = (); - foreach my $perm (@perms) { - if ($perm !~ /:/) { - $all_module_perms{$perm} = 1; - } else { - my ($module, $sub_perm) = split /:/, $perm, 2; - push @{ $sub_perms{$module} }, $sub_perm; - } - } + print $input->redirect( + "/cgi-bin/koha/members/moremember.pl?borrowernumber=$borrowernumber"); +} +else { # fetch for display + my $permissions = Koha::Permissions->search( { parent => undef } ); + $template->param( + patron => $patron, + permissions => $permissions, + ); - # construct flags - my $module_flags = 0; - my $sth=$dbh->prepare("SELECT bit,flag FROM userflags ORDER BY bit"); - $sth->execute(); - while (my ($bit, $flag) = $sth->fetchrow_array) { - if (exists $all_module_perms{$flag}) { - $module_flags += 2**$bit; - } + # Patron details boilerplate for patron related templates + if ( $category_type eq 'C' ) { + my $patron_categories = + Koha::Patron::Categories->search_limited( { category_type => 'A' }, + { order_by => ['categorycode'] } ); + $template->param( 'CATCODE_MULTI' => 1 ) + if $patron_categories->count > 1; + $template->param( 'catcode' => $patron_categories->next ) + if $patron_categories->count == 1; } - - $sth = $dbh->prepare("UPDATE borrowers SET flags=? WHERE borrowernumber=?"); - my $old_flags = $patron->flags // 0; - if( ( $old_flags == 1 || $module_flags == 1 ) && - $old_flags != $module_flags ) { - die "Non-superlibrarian is changing superlibrarian privileges" if !C4::Context->IsSuperLibrarian && C4::Context->preference('ProtectSuperlibrarianPrivileges'); # Interface should not allow this, so we can just die here - } - $sth->execute($module_flags, $member); - - # deal with subpermissions - $sth = $dbh->prepare("DELETE FROM user_permissions WHERE borrowernumber = ?"); - $sth->execute($member); - $sth = $dbh->prepare("INSERT INTO user_permissions (borrowernumber, module_bit, code) - SELECT ?, bit, ? - FROM userflags - WHERE flag = ?"); - foreach my $module (keys %sub_perms) { - next if exists $all_module_perms{$module}; - foreach my $sub_perm (@{ $sub_perms{$module} }) { - $sth->execute($member, $sub_perm, $module); - } - } - - print $input->redirect("/cgi-bin/koha/members/moremember.pl?borrowernumber=$member"); -} else { - - my $accessflags; - my $dbh = C4::Context->dbh(); - # FIXME This needs to be improved to avoid doing the same query - my $sth = $dbh->prepare("select bit,flag from userflags"); - $sth->execute; - while ( my ( $bit, $flag ) = $sth->fetchrow ) { - if ( $bor->{flags} && $bor->{flags} & 2**$bit ) { - $accessflags->{$flag} = 1; - } - } - - my $all_perms = get_all_subpermissions(); - my $user_perms = get_user_subpermissions($bor->{'userid'}); - $sth = $dbh->prepare("SELECT bit, flag FROM userflags ORDER BY bit"); - $sth->execute; - my @loop; - while (my ($bit, $flag) = $sth->fetchrow) { - my $checked=''; - if ($accessflags->{$flag}) { - $checked= 1; - } + $template->param( adultborrower => 1 ) if ( $category_type =~ /^(A|I)$/ ); + $template->param( picture => 1 ) if $patron->image; - my %row = ( bit => $bit, - flag => $flag, - checked => $checked, + if ( C4::Context->preference('ExtendedPatronAttributes') ) { + my $attributes = GetBorrowerAttributes( $bor->{'borrowernumber'} ); + $template->param( + ExtendedPatronAttributes => 1, + extendedattributes => $attributes ); - - my @sub_perm_loop = (); - my $expand_parent = 0; - if ($checked) { - if (exists $all_perms->{$flag}) { - $expand_parent = 1; - foreach my $sub_perm (sort keys %{ $all_perms->{$flag} }) { - push @sub_perm_loop, { - id => "${flag}_$sub_perm", - perm => "$flag:$sub_perm", - code => $sub_perm, - checked => 1 - }; - } - } - } else { - if (exists $user_perms->{$flag}) { - $expand_parent = 1; - # put selected ones first - foreach my $sub_perm (sort keys %{ $user_perms->{$flag} }) { - push @sub_perm_loop, { - id => "${flag}_$sub_perm", - perm => "$flag:$sub_perm", - code => $sub_perm, - checked => 1 - }; - } - } - # then ones not selected - if (exists $all_perms->{$flag}) { - foreach my $sub_perm (sort keys %{ $all_perms->{$flag} }) { - push @sub_perm_loop, { - id => "${flag}_$sub_perm", - perm => "$flag:$sub_perm", - code => $sub_perm, - checked => 0 - } unless exists $user_perms->{$flag} and exists $user_perms->{$flag}->{$sub_perm}; - } - } - } - $row{expand} = $expand_parent; - if ($#sub_perm_loop > -1) { - $row{sub_perm_loop} = \@sub_perm_loop; - } - push @loop, \%row; - } - - if ( $patron->is_child ) { - my $patron_categories = Koha::Patron::Categories->search_limited({ category_type => 'A' }, {order_by => ['categorycode']}); - $template->param( 'CATCODE_MULTI' => 1) if $patron_categories->count > 1; - $template->param( 'catcode' => $patron_categories->next->categorycode ) if $patron_categories->count == 1; } -if (C4::Context->preference('ExtendedPatronAttributes')) { - my $attributes = GetBorrowerAttributes($bor->{'borrowernumber'}); $template->param( - ExtendedPatronAttributes => 1, - extendedattributes => $attributes + borrowernumber => $bor->{'borrowernumber'}, + cardnumber => $bor->{'cardnumber'}, + surname => $bor->{'surname'}, + firstname => $bor->{'firstname'}, + othernames => $bor->{'othernames'}, + categorycode => $bor->{'categorycode'}, + category_type => $category_type, + categoryname => $bor->{'description'}, + address => $bor->{address}, + address2 => $bor->{'address2'}, + streettype => $bor->{streettype}, + city => $bor->{'city'}, + state => $bor->{'state'}, + zipcode => $bor->{'zipcode'}, + country => $bor->{'country'}, + phone => $bor->{'phone'}, + phonepro => $bor->{'phonepro'}, + mobile => $bor->{'mobile'}, + email => $bor->{'email'}, + emailpro => $bor->{'emailpro'}, + branchcode => $bor->{'branchcode'}, + is_child => ( $category_type eq 'C' ), + RoutingSerials => C4::Context->preference('RoutingSerials'), + csrf_token => Koha::Token->new->generate_csrf( + { session_id => scalar $input->cookie('CGISESSID'), } + ), ); -} - -$template->param( - patron => $patron, - loop => \@loop, - csrf_token => - Koha::Token->new->generate_csrf( { session_id => scalar $input->cookie('CGISESSID'), } ), - disable_superlibrarian_privs => C4::Context->preference('ProtectSuperlibrarianPrivileges') ? !C4::Context->IsSuperLibrarian : 0, -); output_html_with_http_headers $input, $cookie, $template->output; -- 2.15.1 (Apple Git-101)