@@ -, +, @@ better manage permissions for testing. -Logged in branch regression test for Intra. -OPAC password login/logout -OPAC anonymous search history preservation. => a module which consists of specific Permissions. Typically covers one Module in Koha. => a specific Permission of a Module. Granting a Borrower the permission to do something. => A specific Permission a specific Borrower has, eg. the permission to edit bibliographic records. -2. Note the permissions a bunch of borrowers has. -1. run updatedatabase.pl which didn't have any subpermissions, a generic subpermission is added and the borrowers should have it if they had the existing module permission. and login persists on other pages. Save. which doesn't have any subpermissions. --- C4/Auth.pm | 71 ++- C4/Members.pm | 11 +- Koha/Auth/BorrowerPermission.pm | 221 +++++++++ Koha/Auth/BorrowerPermissions.pm | 38 ++ Koha/Auth/Permission.pm | 60 +++ Koha/Auth/PermissionManager.pm | 529 +++++++++++++++++++++ Koha/Auth/PermissionModule.pm | 78 +++ Koha/Auth/PermissionModules.pm | 38 ++ Koha/Auth/Permissions.pm | 42 ++ Koha/AuthUtils.pm | 1 - Koha/Schema/Result/Borrower.pm | 71 ++- Koha/Schema/Result/Permission.pm | 79 +-- installer/data/mysql/en/mandatory/userflags.sql | 40 +- .../data/mysql/en/mandatory/userpermissions.sql | 159 ++++--- installer/data/mysql/kohastructure.sql | 94 ++-- installer/data/mysql/updatedatabase.pl | 113 +++++ .../prog/en/modules/members/member-flags.tt | 14 +- members/member-flags.pl | 198 ++++---- misc/devel/interactiveWebDriverShell.pl | 9 +- opac/opac-user.pl | 2 +- t/db_dependent/Koha/Auth.t | 57 ++- t/db_dependent/Koha/Auth/BorrowerPermission.t | 102 ++++ t/db_dependent/Koha/Auth/PermissionManager.t | 221 +++++++++ t/db_dependent/Members/member-flags.t | 100 ++++ t/db_dependent/Opac/opac-search.t | 79 +++ 25 files changed, 2076 insertions(+), 351 deletions(-) create mode 100644 Koha/Auth/BorrowerPermission.pm create mode 100644 Koha/Auth/BorrowerPermissions.pm create mode 100644 Koha/Auth/Permission.pm create mode 100644 Koha/Auth/PermissionManager.pm create mode 100644 Koha/Auth/PermissionModule.pm create mode 100644 Koha/Auth/PermissionModules.pm create mode 100644 Koha/Auth/Permissions.pm create mode 100644 t/db_dependent/Koha/Auth/BorrowerPermission.t create mode 100644 t/db_dependent/Koha/Auth/PermissionManager.t create mode 100644 t/db_dependent/Members/member-flags.t create mode 100644 t/db_dependent/Opac/opac-search.t --- a/C4/Auth.pm +++ a/C4/Auth.pm @@ -23,6 +23,8 @@ use Digest::MD5 qw(md5_base64); use JSON qw/encode_json/; use URI::Escape; use CGI::Session; +use Scalar::Util qw(blessed); +use Try::Tiny; require Exporter; use C4::Context; @@ -226,7 +228,7 @@ sub get_template_and_user { # We are going to use the $flags returned by checkauth # to create the template's parameters that will indicate # which menus the user can access. - if ( $flags && $flags->{superlibrarian} == 1 ) { + if ( $flags && $flags->{superlibrarian} ) { $template->param( CAN_user_circulate => 1 ); $template->param( CAN_user_catalogue => 1 ); $template->param( CAN_user_parameters => 1 ); @@ -992,7 +994,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 @@ -1015,7 +1017,7 @@ 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"; @@ -1741,7 +1743,7 @@ 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 ) { @@ -1758,18 +1760,18 @@ sub checkpw_internal { } $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 ); + $firstname, $surname, $branchcode, $branchname ); return 1, $cardnumber, $userid; } } @@ -1801,6 +1803,7 @@ sub checkpw_hash { } =head2 getuserflags +@DEPRECATED, USE THE Koha::Auth::PermissionManager my $authflags = getuserflags($flags, $userid, [$dbh]); @@ -1813,6 +1816,7 @@ C<$authflags> is a hashref of permissions =cut sub getuserflags { + #@DEPRECATED, USE THE Koha::Auth::PermissionManager my $flags = shift; my $userid = shift; my $dbh = @_ ? shift : C4::Context->dbh; @@ -1824,6 +1828,9 @@ sub getuserflags { no warnings 'numeric'; $flags += 0; } + return get_user_subpermissions($userid); + + #@DEPRECATED, USE THE Koha::Auth::PermissionManager my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags"); $sth->execute; @@ -1847,7 +1854,7 @@ sub getuserflags { } =head2 get_user_subpermissions - +@DEPRECATED, USE THE Koha::Auth::PermissionManager $user_perm_hashref = get_user_subpermissions($userid); Given the userid (note, not the borrowernumber) of a staff user, @@ -1872,25 +1879,22 @@ necessary to check borrowers.flags. =cut sub get_user_subpermissions { + #@DEPRECATED, USE THE Koha::Auth::PermissionManager 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); - + use Koha::Auth::PermissionManager; + my $permissionManager = Koha::Auth::PermissionManager->new(); + my $borrowerPermissions = $permissionManager->getBorrowerPermissions($userid); #Prefetch all related tables. my $user_perms = {}; - while ( my $perm = $sth->fetchrow_hashref ) { - $user_perms->{ $perm->{'flag'} }->{ $perm->{'code'} } = 1; + foreach my $perm ( @$borrowerPermissions ) { + $user_perms->{ $perm->getPermissionModule->module }->{ $perm->getPermission->code } = 1; } + return $user_perms; } =head2 get_all_subpermissions +@DEPRECATED, USE THE Koha::Auth::PermissionManager my $perm_hashref = get_all_subpermissions(); @@ -1903,6 +1907,20 @@ of the subpermission. =cut sub get_all_subpermissions { + #@DEPRECATED, USE THE Koha::Auth::PermissionManager + use Koha::Auth::PermissionManager; + my $permissionManager = Koha::Auth::PermissionManager->new(); + my $all_permissions = $permissionManager->listKohaPermissionsAsHASH(); + foreach my $module ( keys %$all_permissions ) { + my $permissionModule = $all_permissions->{$module}; + foreach my $code (keys %{$permissionModule->{permissions}}) { + my $permission = $permissionModule->{permissions}->{$code}; + $all_permissions->{$module}->{$code} = $permission->{'description'}; + } + } + return $all_permissions; + + #@DEPRECATED, USE THE Koha::Auth::PermissionManager my $dbh = C4::Context->dbh; my $sth = $dbh->prepare( "SELECT flag, code, description FROM permissions @@ -1917,6 +1935,7 @@ sub get_all_subpermissions { } =head2 haspermission +@DEPRECATED, USE THE Koha::Auth::PermissionManager $flags = ($userid, $flagsrequired); @@ -1928,11 +1947,15 @@ Returns member's flags or 0 if a permission is not met. =cut sub haspermission { + #@DEPRECATED, USE THE Koha::Auth::PermissionManager 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( undef, $userid ); + #Sanitate 1 to * because we no longer have 1's from the koha.borrowers.flags. + foreach my $module (%$flagsrequired) { + $flagsrequired->{$module} = '*' if $flagsrequired->{$module} && $flagsrequired->{$module} eq '1'; + } + if ( $userid eq C4::Context->config('user') ) { # Super User Account from /etc/koha.conf @@ -1949,7 +1972,7 @@ sub haspermission { foreach my $module ( keys %$flagsrequired ) { my $subperm = $flagsrequired->{$module}; if ( $subperm eq '*' ) { - return 0 unless ( $flags->{$module} == 1 or ref( $flags->{$module} ) ); + return 0 unless ( ref( $flags->{$module} ) ); } else { return 0 unless ( ( defined $flags->{$module} and --- a/C4/Members.pm +++ a/C4/Members.pm @@ -230,17 +230,8 @@ sub GetMemberDetails { $borrower->{'amountoutstanding'} = $amount; # FIXME - patronflags calls GetMemberAccountRecords... just have patronflags return $amount my $flags = patronflags( $borrower); - my $accessflagshash; - $sth = $dbh->prepare("select bit,flag from userflags"); - $sth->execute; - while ( my ( $bit, $flag ) = $sth->fetchrow ) { - if ( $borrower->{'flags'} && $borrower->{'flags'} & 2**$bit ) { - $accessflagshash->{$flag} = 1; - } - } - $borrower->{'flags'} = $flags; - $borrower->{'authflags'} = $accessflagshash; + $borrower->{'flags'} = $flags; #Is this the flags-column? @DEPRECATED! # For the purposes of making templates easier, we'll define a # 'showname' which is the alternate form the user's first name if --- a/Koha/Auth/BorrowerPermission.pm +++ a/Koha/Auth/BorrowerPermission.pm @@ -0,0 +1,221 @@ +package Koha::Auth::BorrowerPermission; + +# Copyright 2015 Vaara-kirjastot +# +# 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 Scalar::Util qw(blessed); + +use Koha::Auth::BorrowerPermissions; +use Koha::Auth::PermissionModules; +use Koha::Auth::Permissions; +use Koha::Borrowers; + +use Koha::Exception::BadParameter; + +use base qw(Koha::Object); + +sub type { + return 'BorrowerPermission'; +} +sub object_class { + return 'Koha::Auth::BorrowerPermission'; +} + +=head NAME + +Koha::Auth::BorrowerPermission + +=head SYNOPSIS + +Object representation of a Permission given to a Borrower. + +=head new + + my $borrowerPermission = Koha::Auth::BorrowerPermission->new({ + borrowernumber => 12, + permission_module_id => 2, + permission => $Koha::Auth::Permission, + }); + my $borrowerPermission = Koha::Auth::BorrowerPermission->new({ + borrower => $Koha::Borrower, + permissionModule => $Koha::Auth::PermissionModule, + permission_id => 22, + }); + +Remember to ->store() the returned object to persist it in the DB. +@PARAM1 HASHRef of constructor parameters: + MANDATORY keys: + borrower or borrowernumber + permissionModule or permission_module_id + permission or permission_id + Values can be either Koha::Object derivatives or their respective DB primary keys +@RETURNS Koha::Auth::BorrowerPermission +=cut + +sub new { + my ($class, $params) = @_; + + _validateParams($params); + + #Check for duplicates, and update existing permission if available. + my $self = Koha::Auth::BorrowerPermissions->find({borrowernumber => $params->{borrower}->borrowernumber, + permission_module_id => $params->{permissionModule}->permission_module_id, + permission_id => $params->{permission}->permission_id, + }); + $self = $class->SUPER::new() unless $self; + $self->{params} = $params; + $self->set({borrowernumber => $self->getBorrower()->borrowernumber, + permission_id => $self->getPermission()->permission_id, + permission_module_id => $self->getPermissionModule()->permission_module_id + }); + return $self; +} + +=head getBorrower + + my $borrower = $borrowerPermission->getBorrower(); + +@RETURNS Koha::Borrower +=cut + +sub getBorrower { + my ($self) = @_; + + unless ($self->{params}->{borrower}) { + my $dbix_borrower = $self->_result()->borrower; + my $borrower = Koha::Borrower->_new_from_dbic($dbix_borrower); + $self->{params}->{borrower} = $borrower; + } + return $self->{params}->{borrower}; +} + +=head setBorrower + + my $borrowerPermission = $borrowerPermission->setBorrower( $borrower ); + +Set the Borrower. +When setting the DB is automatically updated as well. +@PARAM1 Koha::Borrower, set the given Borrower to this BorrowerPermission. +@RETURNS Koha::Auth::BorrowerPermission, +=cut + +sub setBorrower { + my ($self, $borrower) = @_; + + unless (blessed($borrower) && $borrower->isa('Koha::Borrower')) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->setPermissionModule():> Given parameter '\\$borrower' is not a Koha::Borrower-object!"); + } + $self->{params}->{borrower} = $borrower; + $self->set({borrowernumber => $borrower->borrowernumber()}); + $self->store(); +} + +=head getPermissionModule + + my $permissionModule = $borrowerPermission->getPermissionModule(); + +@RETURNS Koha::Auth::PermissionModule +=cut + +sub getPermissionModule { + my ($self) = @_; + + unless ($self->{params}->{permissionModule}) { + my $dbix_object = $self->_result()->permission_module; + my $object = Koha::Auth::PermissionModule->_new_from_dbic($dbix_object); + $self->{params}->{permissionModule} = $object; + } + return $self->{params}->{permissionModule}; +} + +=head setPermissionModule + + my $borrowerPermission = $borrowerPermission->setPermissionModule( $permissionModule ); + +Set the PermissionModule. +When setting the DB is automatically updated as well. +@PARAM1 Koha::Auth::PermissionModule, set the given PermissionModule as + the PermissionModule of this BorrowePermission. +@RETURNS Koha::Auth::BorrowerPermission, +=cut + +sub setPermissionModule { + my ($self, $permissionModule) = @_; + + unless (blessed($permissionModule) && $permissionModule->isa('Koha::Auth::PermissionModule')) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->setPermissionModule():> Given parameter '\$permissionModule' is not a Koha::Auth::PermissionModule-object!"); + } + $self->{params}->{permissionModule} = $permissionModule; + $self->set({permission_module_id => $permissionModule->permission_module_id()}); + $self->store(); +} + +=head getPermission + + my $permission = $borrowerPermission->getPermission(); + +@RETURNS Koha::Auth::Permission +=cut + +sub getPermission { + my ($self) = @_; + + unless ($self->{params}->{permission}) { + my $dbix_object = $self->_result()->permission; + my $object = Koha::Auth::Permission->_new_from_dbic($dbix_object); + $self->{params}->{permission} = $object; + } + return $self->{params}->{permission}; +} + +=head setPermission + + my $borrowerPermission = $borrowerPermission->setPermission( $permission ); + +Set the Permission. +When setting the DB is automatically updated as well. +@PARAM1 Koha::Auth::Permission, set the given Permission to this BorrowerPermission. +@RETURNS Koha::Auth::BorrowerPermission, +=cut + +sub setPermission { + my ($self, $permission) = @_; + + unless (blessed($permission) && $permission->isa('Koha::Auth::Permission')) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->setPermission():> Given parameter '\$permission' is not a Koha::Auth::Permission-object!"); + } + $self->{params}->{permission} = $permission; + $self->set({permission_id => $permission->permission_id()}); + $self->store(); +} + +=head _validateParams + +Validates the given constructor parameters and fetches the Koha::Objects when needed. + +=cut + +sub _validateParams { + my ($params) = @_; + + $params->{permissionModule} = Koha::Auth::PermissionModules->cast( $params->{permission_module_id} || $params->{permissionModule} ); + $params->{permission} = Koha::Auth::Permissions->cast( $params->{permission_id} || $params->{permission} ); + $params->{borrower} = Koha::Borrowers->cast( $params->{borrowernumber} || $params->{borrower} ); +} + +1; --- a/Koha/Auth/BorrowerPermissions.pm +++ a/Koha/Auth/BorrowerPermissions.pm @@ -0,0 +1,38 @@ +package Koha::Auth::BorrowerPermissions; + +# Copyright 2015 Vaara-kirjastot +# +# 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 Scalar::Util qw(blessed); + +use Koha::Auth::BorrowerPermission; + +use base qw(Koha::Objects); + +sub type { + return 'BorrowerPermission'; +} +sub object_class { + return 'Koha::Auth::BorrowerPermission'; +} + +sub _get_castable_unique_columns { + return ['borrower_permission_id']; +} + +1; --- a/Koha/Auth/Permission.pm +++ a/Koha/Auth/Permission.pm @@ -0,0 +1,60 @@ +package Koha::Auth::Permission; + +# Copyright 2015 Vaara-kirjastot +# +# 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 Koha::Auth::Permissions; + +use Koha::Exception::BadParameter; + +use base qw(Koha::Object); + +sub type { + return 'Permission'; +} +sub object_class { + return 'Koha::Auth::Permission'; +} + +sub new { + my ($class, $params) = @_; + + _validateParams($params); + + my $self = Koha::Auth::Permissions->find({code => $params->{code}, module => $params->{module}}); + $self = $class->SUPER::new() unless $self; + $self->set($params); + return $self; +} + +sub _validateParams { + my ($params) = @_; + + unless ($params->{description} && length $params->{description} > 0) { + Koha::Exception::BadParameter->throw(error => "Koha::Auth::Permission->new():> Parameter 'description' isn't defined or is empty."); + } + unless ($params->{module} && length $params->{module} > 0) { + Koha::Exception::BadParameter->throw(error => "Koha::Auth::Permission->new():> Parameter 'module' isn't defined or is empty."); + } + unless ($params->{code} && length $params->{code} > 0) { + Koha::Exception::BadParameter->throw(error => "Koha::Auth::Permission->new():> Parameter 'code' isn't defined or is empty."); + } +} + +1; --- a/Koha/Auth/PermissionManager.pm +++ a/Koha/Auth/PermissionManager.pm @@ -0,0 +1,529 @@ +package Koha::Auth::PermissionManager; + +# Copyright 2015 Vaara-kirjastot +# +# 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 Scalar::Util qw(blessed); +use Try::Tiny; + +use Koha::Database; +use Koha::Auth::Permission; +use Koha::Auth::PermissionModule; +use Koha::Auth::BorrowerPermission; +use Koha::Auth::BorrowerPermissions; + +use Koha::Exception::BadParameter; +use Koha::Exception::NoPermission; +use Koha::Exception::UnknownProgramState; + +=head NAME Koha::Auth::PermissionManager + +=head SYNOPSIS + +PermissionManager is a gateway to all Koha's permission operations. You shouldn't +need to touch individual Koha::Auth::Permission* -objects. + +=head USAGE + +See t::db_dependent::Koha::Auth::PermissionManager.t + +=head new + + my $permissionManager = Koha::Auth::PermissionManager->new(); + +Instantiates a new PemissionManager. + +In the future this Manager can easily be improved with Koha::Cache. + +=cut + +sub new { + my ($class, $self) = @_; + $self = {} unless $self; + bless($self, $class); + return $self; +} + +=head addPermission + + $permissionManager->addPermission({ code => "end_remaining_hostilities", + description => "All your base are belong to us", + }); + +INSERTs or UPDATEs a Koha::Auth::Permission to the Koha DB. +Very handy when introducing new features that need new permissions. + +@PARAM1 Koha::Auth::Permission + or + HASHRef of all the koha.permissions-table columns set. +@THROWS Koha::Exception::BadParameter +=cut + +sub addPermission { + my ($self, $permission) = @_; + if (blessed($permission) && not($permission->isa('Koha::Auth::Permission'))) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."::addPermission():> Given permission is not a Koha::Auth::Permission-object."); + } + elsif (ref($permission) eq 'HASH') { + $permission = Koha::Auth::Permission->new($permission); + } + unless (blessed($permission)) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."::addPermission():> Given permission '$permission' is not of a recognized format."); + } + + $permission->store(); +} + +=head getPermission + + my $permission = $permissionManager->getPermission('edit_items'); #koha.permissions.code + my $permission = $permissionManager->getPermission(12); #koha.permissions.permission_id + my $permission = $permissionManager->getPermission($dbix_Permission); #Koha::Schema::Result::Permission + +@RETURNS Koha::Auth::Permission-object +@THROWS Koha::Exception::BadParameter +=cut + +sub getPermission { + my ($self, $permissionId) = @_; + + try { + return Koha::Auth::Permissions->cast($permissionId); + } catch { + if (blessed($_) && $_->isa('Koha::Exception::UnknownObject')) { + #We catch this type of exception, and simply return nothing, since there was no such Permission + } + else { + die $_; + } + }; +} + +=head delPermission + + $permissionManager->delPermission('edit_items'); #koha.permissions.code + $permissionManager->delPermission(12); #koha.permissions.permission_id + $permissionManager->delPermission($dbix_Permission); #Koha::Schema::Result::Permission + $permissionManager->delPermission($permission); #Koha::Auth::Permission + +@THROWS Koha::Exception::UnknownObject if no given object in DB to delete. +=cut + +sub delPermission { + my ($self, $permissionId) = @_; + + my $permission = Koha::Auth::Permissions->cast($permissionId); + $permission->delete(); +} + +=head addPermissionModule + + $permissionManager->addPermissionModule({ module => "scotland", + description => "William Wallace is my hero!", + }); + +INSERTs or UPDATEs a Koha::Auth::PermissionModule to the Koha DB. +Very handy when introducing new features that need new permissions. + +@PARAM1 Koha::Auth::PermissionModule + or + HASHRef of all the koha.permission_modules-table columns set. +@THROWS Koha::Exception::BadParameter +=cut + +sub addPermissionModule { + my ($self, $permissionModule) = @_; + if (blessed($permissionModule) && not($permissionModule->isa('Koha::Auth::PermissionModule'))) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."::addPermission():> Given permissionModule is not a Koha::Auth::PermissionModule-object."); + } + elsif (ref($permissionModule) eq 'HASH') { + $permissionModule = Koha::Auth::PermissionModule->new($permissionModule); + } + unless (blessed($permissionModule)) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."::addPermission():> Given permissionModule '$permissionModule' is not of a recognized format."); + } + + $permissionModule->store(); +} + +=head getPermissionModule + + my $permission = $permissionManager->getPermissionModule('cataloguing'); #koha.permission_modules.module + my $permission = $permissionManager->getPermission(12); #koha.permission_modules.permission_module_id + my $permission = $permissionManager->getPermission($dbix_Permission); #Koha::Schema::Result::PermissionModule + +@RETURNS Koha::Auth::PermissionModule-object +@THROWS Koha::Exception::BadParameter +=cut + +sub getPermissionModule { + my ($self, $permissionModuleId) = @_; + + try { + return Koha::Auth::PermissionModules->cast($permissionModuleId); + } catch { + if (blessed($_) && $_->isa('Koha::Exception::UnknownObject')) { + #We catch this type of exception, and simply return nothing, since there was no such PermissionModule + } + else { + die $_; + } + }; +} + +=head delPermissionModule + + $permissionManager->delPermissionModule('cataloguing'); #koha.permission_modules.module + $permissionManager->delPermissionModule(12); #koha.permission_modules.permission_module_id + $permissionManager->delPermissionModule($dbix_Permission); #Koha::Schema::Result::PermissionModule + $permissionManager->delPermissionModule($permissionModule); #Koha::Auth::PermissionModule + +@THROWS Koha::Exception::UnknownObject if no given object in DB to delete. +@THROWS Koha::Exception::BadParameter +=cut + +sub delPermissionModule { + my ($self, $permissionModuleId) = @_; + + my $permissionModule = Koha::Auth::PermissionModules->cast($permissionModuleId); + $permissionModule->delete(); +} + +=head getKohaPermissions + + my $kohaPermissions = $permissionManager->getKohaPermissions(); + +Gets all the PermissionModules and their related Permissions in one huge DB query. +@RETURNS ARRAYRef of Koha::Auth::PermissionModule-objects with related objects prefetched. +=cut + +sub getKohaPermissions { + my ($self) = @_; + + my $schema = Koha::Database->new()->schema(); + my @permissionModules = $schema->resultset('PermissionModule')->search( + {}, + { join => ['permissions'], + prefetch => ['permissions'], + order_by => ['me.module', 'permissions.code'], + } + ); + #Cast DBIx to Koha::Object. + for (my $i=0 ; $icast( $permissionModules[$i] ); + } + return \@permissionModules; +} + +=head listKohaPermissionsAsHASH + +@RETURNS HASHRef, a HASH-representation of all the permissions and permission modules +in Koha. Eg: + { + acquisitions => { + description => "Yada yada", + module => 'acquisitions', + permission_module_id => 21, + permissions => { + budget_add_del => { + description => "More yada yada", + code => 'budget_add_del', + permission_id => 12, + } + budget_manage => { + description => "Yaawn yadayawn", + ... + } + ... + } + }, + borrowers => { + ... + }, + ... + } +=cut + +sub listKohaPermissionsAsHASH { + my ($self) = @_; + my $permissionModules = $self->getKohaPermissions(); + my $hash = {}; + + foreach my $permissionModule (sort {$a->module cmp $b->module} @$permissionModules) { + my $module = $permissionModule->module; + + $hash->{$module} = $permissionModule->_result->{'_column_data'}; + $hash->{$module}->{permissions} = {}; + + my $permissions = $permissionModule->getPermissions; + foreach my $permission (sort {$a->code cmp $b->code} @$permissions) { + my $code = $permission->code; + + $hash->{$module}->{permissions}->{$code} = $permission->_result->{'_column_data'}; + } + } + return $hash; +} + +=head getBorrowerPermissions + + my $borrowerPermissions = $permissionManager->getBorrowerPermissions($borrower); #Koha::Borrower + my $borrowerPermissions = $permissionManager->getBorrowerPermissions($dbix_borrower);#Koha::Schema::Resultset::Borrower + my $borrowerPermissions = $permissionManager->getBorrowerPermissions(1012); #koha.borrowers.borrowernumber + my $borrowerPermissions = $permissionManager->getBorrowerPermissions('167A0012311'); #koha.borrowers.cardnumber + my $borrowerPermissions = $permissionManager->getBorrowerPermissions('bill69'); #koha.borrowers.userid + +@RETURNS ARRAYRef of Koha::Auth::BorrowerPermission-objects +@THROWS Koha::Exception::UnknownObject, if the given $borrower cannot be casted to Koha::Borrower +@THROWS Koha::Exception::BadParameter +=cut + +sub getBorrowerPermissions { + my ($self, $borrower) = @_; + $borrower = Koha::Borrowers->cast($borrower); + + if ($borrower->isSuperuser()) { + return [Koha::Auth::BorrowerPermission->new({borrower => $borrower, permission => 'superlibrarian', permissionModule => 'superlibrarian'})]; + } + + my $schema = Koha::Database->new()->schema(); + + my @borrowerPermissions = $schema->resultset('BorrowerPermission')->search({borrowernumber => $borrower->borrowernumber}, + {join => ['permission','permission_module'], + prefetch => ['permission','permission_module'], + order_by => ['permission_module.module', 'permission.code']}); + for (my $i=0 ; $icast($borrowerPermissions[$i]); + } + return \@borrowerPermissions; +} + +=head grantPermissions + + $permissionManager->grantPermissions($borrower, {borrowers => 'view_borrowers', + reserveforothers => ['place_holds'], + tools => ['edit_news', 'edit_notices'], + acquisition => { + budger_add_del => 1, + budget_modify => 1, + }, + } + ); + +Adds a group of permissions to one user. +@THROWS Koha::Exception::UnknownObject, if the given $borrower cannot be casted to Koha::Borrower +@THROWS Koha::Exception::BadParameter +=cut + +sub grantPermissions { + my ($self, $borrower, $permissionsGroup) = @_; + + while (my ($module, $permissions) = each(%$permissionsGroup)) { + if (ref($permissions) eq 'ARRAY') { + foreach my $permission (@$permissions) { + $self->grantPermission($borrower, $module, $permission); + } + } + elsif (ref($permissions) eq 'HASH') { + foreach my $permission (keys(%$permissions)) { + $self->grantPermission($borrower, $module, $permission); + } + } + else { + $self->grantPermission($borrower, $module, $permissions); + } + } +} + +=head grantPermission + + my $borrowerPermission = $permissionManager->grantPermission($borrower, $permissionModule, $permission); + +@PARAM1 Koha::Borrower or + Scalar koha.borrowers.borrowernumber or + Scalar koha.borrowers.cardnumber or + Scalar koha.borrowers.userid or +@PARAM2 Koha::Auth::PermissionModule-object + Scalar koha.permission_modules.module or + Scalar koha.permission_modules.permission_module_id +@PARAM3 Koha::Auth::Permission-object or + Scalar koha.permissions.code or + Scalar koha.permissions.permission_id +@RETURNS Koha::Auth::BorrowerPermissions +@THROWS Koha::Exception::UnknownObject, if the given parameters cannot be casted to Koha::Object-subclasses +@THROWS Koha::Exception::BadParameter +=cut + +sub grantPermission { + my ($self, $borrower, $permissionModule, $permission) = @_; + + my $borrowerPermission = Koha::Auth::BorrowerPermission->new({borrower => $borrower, permissionModule => $permissionModule, permission => $permission}); + $borrowerPermission->store(); + return $borrowerPermission; +} + +=head + + $permissionManager->revokePermission($borrower, $permissionModule, $permission); + +Revokes a Permission from a Borrower +same parameters as grantPermission() + +@THROWS Koha::Exception::UnknownObject, if the given parameters cannot be casted to Koha::Object-subclasses +@THROWS Koha::Exception::BadParameter +=cut + +sub revokePermission { + my ($self, $borrower, $permissionModule, $permission) = @_; + + my $borrowerPermission = Koha::Auth::BorrowerPermission->new({borrower => $borrower, permissionModule => $permissionModule, permission => $permission}); + $borrowerPermission->delete(); + return $borrowerPermission; +} + +=head revokeAllPermissions + + $permissionManager->revokeAllPermissions($borrower); + +@THROWS Koha::Exception::UnknownObject, if the given $borrower cannot be casted to Koha::Borrower +@THROWS Koha::Exception::BadParameter +=cut + +sub revokeAllPermissions { + my ($self, $borrower) = @_; + $borrower = Koha::Borrowers->cast($borrower); + + my $schema = Koha::Database->new()->schema(); + $schema->resultset('BorrowerPermission')->search({borrowernumber => $borrower->borrowernumber})->delete_all(); +} + +=head hasPermissions + +See if the given Borrower has all of the given permissions +@PARAM1 Koha::Borrower, or any of the koha.borrowers-table's unique identifiers. +@PARAM2 HASHRef of needed permissions, + { + borrowers => 'view_borrowers', + reserveforothers => ['place_holds'], + tools => ['edit_news', 'edit_notices'], + acquisition => { + budger_add_del => 1, + budget_modify => 1, + }, + coursereserves => '*', #Means any Permission under this PermissionModule + } +@RETURNS see hasPermission() +@THROWS Koha::Exception::NoPermission, from hasPermission() if permission is missing. +=cut + +sub hasPermissions { + my ($self, $borrower, $requiredPermissions) = @_; + + foreach my $module (keys(%$requiredPermissions)) { + my $permissions = $requiredPermissions->{$module}; + if (ref($permissions) eq 'ARRAY') { + foreach my $permission (@$permissions) { + $self->hasPermission($borrower, $module, $permission); + } + } + elsif (ref($permissions) eq 'HASH') { + foreach my $permission (keys(%$permissions)) { + $self->hasPermission($borrower, $module, $permission); + } + } + else { + $self->hasPermission($borrower, $module, $permissions); + } + } + return 1; +} + +=head hasPermission + +See if the given Borrower has the given permission +@PARAM1 Koha::Borrower, or any of the koha.borrowers-table's unique identifiers. +@PARAM2 Koha::Auth::PermissionModule or koha.permission_modules.module or koha.permission_modules.permission_module_id +@PARAM3 Koha::Auth::Permission or koha.permissions.code or koha.permissions.permission_id or + '*' if we just need any permission for the given PermissionModule. +@RETURNS Integer, 1 if permission check succeeded. + 2 if user is a superlibrarian. + Catch Exceptions if permission check fails. +@THROWS Koha::Exception::NoPermission, if Borrower is missing the permission. + Exception tells which permission is missing. +@THROWS Koha::Exception::UnknownObject, if the given parameters cannot be casted to Koha::Object-subclasses +@THROWS Koha::Exception::BadParameter +=cut + +sub hasPermission { + my ($self, $borrower, $permissionModule, $permission) = @_; + + $borrower = Koha::Borrowers->cast($borrower); + $permissionModule = Koha::Auth::PermissionModules->cast($permissionModule); + $permission = Koha::Auth::Permissions->cast($permission) unless $permission eq '*'; + + my $error; + if ($permission eq '*') { + my $borrowerPermission = Koha::Auth::BorrowerPermissions->search({borrowernumber => $borrower->borrowernumber, + permission_module_id => $permissionModule->permission_module_id, + })->next(); + return 1 if ($borrowerPermission); + $error = "Borrower '".$borrower->borrowernumber."' lacks any permission under permission module '".$permissionModule->module."'."; + } + else { + my $borrowerPermission = Koha::Auth::BorrowerPermissions->search({borrowernumber => $borrower->borrowernumber, + permission_module_id => $permissionModule->permission_module_id, + permission_id => $permission->permission_id, + })->next(); + return 1 if ($borrowerPermission); + $error = "Borrower '".$borrower->borrowernumber."' lacks permission module '".$permissionModule->module."' and permission '".$permission->code."'."; + } + + return 2 if not($permissionModule->module eq 'superlibrarian') && $self->_isSuperuser($borrower); + return 2 if not($permissionModule->module eq 'superlibrarian') && $self->_isSuperlibrarian($borrower); + Koha::Exception::NoPermission->throw(error => $error); +} + +sub _isSuperuser { + my ($self, $borrower) = @_; + $borrower = Koha::Borrowers->cast($borrower); + + if ( $borrower->userid && $borrower->userid eq C4::Context->config('user') ) { + return 1; + } + elsif ( $borrower->userid && $borrower->userid eq 'demo' && C4::Context->config('demo') ) { + return 1; + } + return 0; +} + +sub _isSuperlibrarian { + my ($self, $borrower) = @_; + + try { + return $self->hasPermission($borrower, 'superlibrarian', 'superlibrarian'); + } catch { + if (blessed($_) && $_->isa('Koha::Exception::NoPermission')) { + return 0; + } + else { + die $_; + } + }; +} + +1; --- a/Koha/Auth/PermissionModule.pm +++ a/Koha/Auth/PermissionModule.pm @@ -0,0 +1,78 @@ +package Koha::Auth::PermissionModule; + +# Copyright 2015 Vaara-kirjastot +# +# 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 Koha::Auth::PermissionModules; + +use Koha::Exception::BadParameter; + +use base qw(Koha::Object); + +sub type { + return 'PermissionModule'; +} +sub object_class { + return 'Koha::Auth::PermissionModule'; +} + +sub new { + my ($class, $params) = @_; + + _validateParams($params); + + my $self = Koha::Auth::PermissionModules->find({module => $params->{module}}); + $self = $class->SUPER::new() unless $self; + $self->set($params); + return $self; +} + +sub _validateParams { + my ($params) = @_; + + unless ($params->{description} && length $params->{description} > 0) { + Koha::Exception::BadParameter->throw(error => "Koha::Auth::Permission->new():> Parameter 'description' isn't defined or is empty."); + } + unless ($params->{module} && length $params->{module} > 0) { + Koha::Exception::BadParameter->throw(error => "Koha::Auth::Permission->new():> Parameter 'module' isn't defined or is empty."); + } +} + +=head getPermissions + + my $permissions = $permissionModule->getPermissions(); + +@RETURNS List of Koha::Auth::Permission-objects +=cut + +sub getPermissions { + my ($self) = @_; + + unless ($self->{params}->{permissions}) { + $self->{params}->{permissions} = []; + my @dbix_objects = $self->_result()->permissions; + foreach my $dbix_object (@dbix_objects) { + my $object = Koha::Auth::Permission->_new_from_dbic($dbix_object); + push @{$self->{params}->{permissions}}, $object; + } + } + return $self->{params}->{permissions}; +} + +1; --- a/Koha/Auth/PermissionModules.pm +++ a/Koha/Auth/PermissionModules.pm @@ -0,0 +1,38 @@ +package Koha::Auth::PermissionModules; + +# Copyright 2015 Vaara-kirjastot +# +# 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 Scalar::Util qw(blessed); + +use Koha::Auth::PermissionModule; + +use base qw(Koha::Objects); + +sub type { + return 'PermissionModule'; +} +sub object_class { + return 'Koha::Auth::PermissionModule'; +} + +sub _get_castable_unique_columns { + return ['permission_module_id', 'module']; +} + +1; --- a/Koha/Auth/Permissions.pm +++ a/Koha/Auth/Permissions.pm @@ -0,0 +1,42 @@ +package Koha::Auth::Permissions; + +# Copyright 2015 Vaara-kirjastot +# +# 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 Scalar::Util qw(blessed); + +use Koha::Auth::Permission; + +use Koha::Exception::BadParameter; +use Koha::Exception::UnknownObject; + +use base qw(Koha::Objects); + +sub type { + return 'Permission'; +} + +sub object_class { + return 'Koha::Auth::Permission'; +} + +sub _get_castable_unique_columns { + return ['permission_id', 'code']; +} + +1; --- a/Koha/AuthUtils.pm +++ a/Koha/AuthUtils.pm @@ -166,7 +166,6 @@ sub _createTemporarySuperuser { firstname => $superuserName, surname => $superuserName, branchcode => 'NO_LIBRARY_SET', - flags => 1, email => C4::Context->preference('KohaAdminEmailAddress') }); return $borrower; --- a/Koha/Schema/Result/Borrower.pm +++ a/Koha/Schema/Result/Borrower.pm @@ -309,11 +309,6 @@ __PACKAGE__->table("borrowers"); is_nullable: 1 size: 60 -=head2 flags - - data_type: 'integer' - is_nullable: 1 - =head2 userid data_type: 'varchar' @@ -542,8 +537,6 @@ __PACKAGE__->add_columns( { data_type => "varchar", is_nullable => 1, size => 1 }, "password", { data_type => "varchar", is_nullable => 1, size => 60 }, - "flags", - { data_type => "integer", is_nullable => 1 }, "userid", { data_type => "varchar", is_nullable => 1, size => 75 }, "opacnote", @@ -648,6 +641,36 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); +=head2 api_keys + +Type: has_many + +Related object: L + +=cut + +__PACKAGE__->has_many( + "api_keys", + "Koha::Schema::Result::ApiKey", + { "foreign.borrowernumber" => "self.borrowernumber" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + +=head2 api_timestamp + +Type: might_have + +Related object: L + +=cut + +__PACKAGE__->might_have( + "api_timestamp", + "Koha::Schema::Result::ApiTimestamp", + { "foreign.borrowernumber" => "self.borrowernumber" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + =head2 aqbasketusers Type: has_many @@ -753,6 +776,21 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); +=head2 borrower_permissions + +Type: has_many + +Related object: L + +=cut + +__PACKAGE__->has_many( + "borrower_permissions", + "Koha::Schema::Result::BorrowerPermission", + { "foreign.borrowernumber" => "self.borrowernumber" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + =head2 borrower_syncs Type: has_many @@ -1053,21 +1091,6 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 user_permissions - -Type: has_many - -Related object: L - -=cut - -__PACKAGE__->has_many( - "user_permissions", - "Koha::Schema::Result::UserPermission", - { "foreign.borrowernumber" => "self.borrowernumber" }, - { cascade_copy => 0, cascade_delete => 0 }, -); - =head2 virtualshelfcontents Type: has_many @@ -1154,8 +1177,8 @@ Composing rels: L -> ordernumber __PACKAGE__->many_to_many("ordernumbers", "aqorder_users", "ordernumber"); -# Created by DBIx::Class::Schema::Loader v0.07039 @ 2015-04-27 16:08:40 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Z50zYBD3Hqlv5/EnoLnyZw +# Created by DBIx::Class::Schema::Loader v0.07039 @ 2015-07-11 12:59:56 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZRxBjZb0KKxabonVI/2vjg # You can replace this text with custom content, and it will be preserved on regeneration --- a/Koha/Schema/Result/Permission.pm +++ a/Koha/Schema/Result/Permission.pm @@ -23,17 +23,22 @@ __PACKAGE__->table("permissions"); =head1 ACCESSORS -=head2 module_bit +=head2 permission_id data_type: 'integer' - default_value: 0 + is_auto_increment: 1 + is_nullable: 0 + +=head2 module + + data_type: 'varchar' is_foreign_key: 1 is_nullable: 0 + size: 32 =head2 code data_type: 'varchar' - default_value: (empty string) is_nullable: 0 size: 64 @@ -46,15 +51,12 @@ __PACKAGE__->table("permissions"); =cut __PACKAGE__->add_columns( - "module_bit", - { - data_type => "integer", - default_value => 0, - is_foreign_key => 1, - is_nullable => 0, - }, + "permission_id", + { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, + "module", + { data_type => "varchar", is_foreign_key => 1, is_nullable => 0, size => 32 }, "code", - { data_type => "varchar", default_value => "", is_nullable => 0, size => 64 }, + { data_type => "varchar", is_nullable => 0, size => 64 }, "description", { data_type => "varchar", is_nullable => 1, size => 255 }, ); @@ -63,7 +65,19 @@ __PACKAGE__->add_columns( =over 4 -=item * L +=item * L + +=back + +=cut + +__PACKAGE__->set_primary_key("permission_id"); + +=head1 UNIQUE CONSTRAINTS + +=head2 C + +=over 4 =item * L @@ -71,46 +85,43 @@ __PACKAGE__->add_columns( =cut -__PACKAGE__->set_primary_key("module_bit", "code"); +__PACKAGE__->add_unique_constraint("code", ["code"]); =head1 RELATIONS -=head2 module_bit +=head2 borrower_permissions -Type: belongs_to +Type: has_many -Related object: L +Related object: L =cut -__PACKAGE__->belongs_to( - "module_bit", - "Koha::Schema::Result::Userflag", - { bit => "module_bit" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, +__PACKAGE__->has_many( + "borrower_permissions", + "Koha::Schema::Result::BorrowerPermission", + { "foreign.permission_id" => "self.permission_id" }, + { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 user_permissions +=head2 module -Type: has_many +Type: belongs_to -Related object: L +Related object: L =cut -__PACKAGE__->has_many( - "user_permissions", - "Koha::Schema::Result::UserPermission", - { - "foreign.code" => "self.code", - "foreign.module_bit" => "self.module_bit", - }, - { cascade_copy => 0, cascade_delete => 0 }, +__PACKAGE__->belongs_to( + "module", + "Koha::Schema::Result::PermissionModule", + { module => "module" }, + { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, ); -# Created by DBIx::Class::Schema::Loader v0.07025 @ 2013-10-14 20:56:21 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Ut3lzlxoPPoIIwmhJViV1Q +# Created by DBIx::Class::Schema::Loader v0.07039 @ 2015-07-28 17:08:28 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ugaurNx48ISDyRgoAlBk2A # You can replace this text with custom content, and it will be preserved on regeneration --- a/installer/data/mysql/en/mandatory/userflags.sql +++ a/installer/data/mysql/en/mandatory/userflags.sql @@ -1,21 +1,21 @@ -INSERT INTO userflags (bit, flag, flagdesc, defaulton) VALUES -(0,'superlibrarian','Access to all librarian functions',0), -(1,'circulate','Check out and check in items',0), -(2,'catalogue','Required for staff login. Staff access, allows viewing of catalogue in staff client.',0), -(3,'parameters','Manage Koha system settings (Administration panel)',0), -(4,'borrowers','Add or modify patrons',0), -(5,'permissions','Set user permissions',0), -(6,'reserveforothers','Place and modify holds for patrons',0), -(9,'editcatalogue','Edit catalog (Modify bibliographic/holdings data)',0), -(10,'updatecharges','Manage patrons fines and fees',0), -(11,'acquisition','Acquisition and/or suggestion management',0), -(12,'management','Set library management parameters (deprecated)',0), -(13,'tools','Use all tools (expand for granular tools permissions)',0), -(14,'editauthorities','Edit authorities',0), -(15,'serials','Manage serial subscriptions',0), -(16,'reports','Allow access to the reports module',0), -(17,'staffaccess','Allow staff members to modify permissions for other staff members',0), -(18,'coursereserves','Course reserves',0), -(19, 'plugins', 'Koha plugins', '0'), -(20, 'lists', 'Lists', 0) +INSERT INTO permission_modules (module, description) VALUES +('superlibrarian','Access to all librarian functions'), +('circulate','Check out and check in items'), +('catalogue','Required for staff login. Staff access, allows viewing of catalogue in staff client.'), +('parameters','Manage Koha system settings (Administration panel)'), +('borrowers','Add or modify patrons'), +('permissions','Set user permissions'), +('reserveforothers','Place and modify holds for patrons'), +('editcatalogue','Edit catalog (Modify bibliographic/holdings data)'), +('updatecharges','Manage patrons fines and fees'), +('acquisition','Acquisition and/or suggestion management'), +('management','Set library management parameters (deprecated)'), +('tools','Use all tools (expand for granular tools permissions)'), +('editauthorities','Edit authorities'), +('serials','Manage serial subscriptions'), +('reports','Allow access to the reports module'), +('staffaccess','Allow staff members to modify permissions for other staff members'), +('coursereserves','Course reserves'), +('plugins', 'Koha plugins'), +('lists', 'Lists') ; --- a/installer/data/mysql/en/mandatory/userpermissions.sql +++ a/installer/data/mysql/en/mandatory/userpermissions.sql @@ -1,77 +1,84 @@ -INSERT INTO permissions (module_bit, code, description) VALUES - ( 1, 'circulate_remaining_permissions', 'Remaining circulation permissions'), - ( 1, 'override_renewals', 'Override blocked renewals'), - ( 1, 'overdues_report', 'Execute overdue items report'), - ( 1, 'force_checkout', 'Force checkout if a limitation exists'), - ( 1, 'manage_restrictions', 'Manage restrictions for accounts'), - ( 3, 'parameters_remaining_permissions', 'Remaining system parameters permissions'), - ( 3, 'manage_circ_rules', 'manage circulation rules'), - ( 6, 'place_holds', 'Place holds for patrons'), - ( 6, 'modify_holds_priority', 'Modify holds priority'), - ( 9, 'edit_catalogue', 'Edit catalog (Modify bibliographic/holdings data)'), - ( 9, 'fast_cataloging', 'Fast cataloging'), - ( 9, 'edit_items', 'Edit items'), - ( 9, 'edit_items_restricted', 'Limit item modification to subfields defined in the SubfieldsToAllowForRestrictedEditing preference (please note that edit_item is still required)'), - ( 9, 'delete_all_items', 'Delete all items at once'), - (10, 'writeoff', 'Write off fines and fees'), - (10, 'remaining_permissions', 'Remaining permissions for managing fines and fees'), - (11, 'vendors_manage', 'Manage vendors'), - (11, 'contracts_manage', 'Manage contracts'), - (11, 'period_manage', 'Manage periods'), - (11, 'budget_manage', 'Manage budgets'), - (11, 'budget_modify', 'Modify budget (can''t create lines, but can modify existing ones)'), - (11, 'planning_manage', 'Manage budget plannings'), - (11, 'order_manage', 'Manage orders & basket'), - (11, 'order_manage_all', 'Manage all orders and baskets, regardless of restrictions on them'), - (11, 'group_manage', 'Manage orders & basketgroups'), - (11, 'order_receive', 'Manage orders & basket'), - (11, 'budget_add_del', 'Add and delete budgets (but can''t modify budgets)'), - (11, 'budget_manage_all', 'Manage all budgets'), - (13, 'edit_news', 'Write news for the OPAC and staff interfaces'), - (13, 'label_creator', 'Create printable labels and barcodes from catalog and patron data'), - (13, 'edit_calendar', 'Define days when the library is closed'), - (13, 'moderate_comments', 'Moderate patron comments'), - (13, 'edit_notices', 'Define notices'), - (13, 'edit_notice_status_triggers', 'Set notice/status triggers for overdue items'), - (13, 'edit_quotes', 'Edit quotes for quote-of-the-day feature'), - (13, 'view_system_logs', 'Browse the system logs'), - (13, 'inventory', 'Perform inventory (stocktaking) of your catalog'), - (13, 'stage_marc_import', 'Stage MARC records into the reservoir'), - (13, 'manage_staged_marc', 'Managed staged MARC records, including completing and reversing imports'), - (13, 'export_catalog', 'Export bibliographic and holdings data'), - (13, 'import_patrons', 'Import patron data'), - (13, 'edit_patrons', 'Perform batch modification of patrons'), - (13, 'delete_anonymize_patrons', 'Delete old borrowers and anonymize circulation history (deletes borrower reading history)'), - (13, 'batch_upload_patron_images', 'Upload patron images in a batch or one at a time'), - (13, 'schedule_tasks', 'Schedule tasks to run'), - (13, 'items_batchmod', 'Perform batch modification of items'), - (13, 'items_batchmod_restricted', 'Limit batch item modification to subfields defined in the SubfieldsToAllowForRestrictedBatchmod preference (please note that items_batchmod is still required)'), - (13, 'items_batchdel', 'Perform batch deletion of items'), - (13, 'manage_csv_profiles', 'Manage CSV export profiles'), - (13, 'moderate_tags', 'Moderate patron tags'), - (13, 'rotating_collections', 'Manage rotating collections'), - (13, 'upload_local_cover_images', 'Upload local cover images'), - (13, 'manage_patron_lists', 'Add, edit and delete patron lists and their contents'), - (13, 'records_batchmod', 'Perform batch modification of records (biblios or authorities)'), - (13, 'marc_modification_templates', 'Manage marc modification templates'), - (13, 'records_batchdel', 'Perform batch deletion of records (bibliographic or authority)'), - (15, 'check_expiration', 'Check the expiration of a serial'), - (15, 'claim_serials', 'Claim missing serials'), - (15, 'create_subscription', 'Create a new subscription'), - (15, 'delete_subscription', 'Delete an existing subscription'), - (15, 'edit_subscription', 'Edit an existing subscription'), - (15, 'receive_serials', 'Serials receiving'), - (15, 'renew_subscription', 'Renew a subscription'), - (15, 'routing', 'Routing'), - (15, 'superserials', 'Manage subscriptions from any branch (only applies when IndependentBranches is used)'), - (16, 'execute_reports', 'Execute SQL reports'), - (16, 'create_reports', 'Create SQL reports'), - (18, 'manage_courses', 'Add, edit and delete courses'), - (18, 'add_reserves', 'Add course reserves'), - (18, 'delete_reserves', 'Remove course reserves'), - (19, 'manage', 'Manage plugins ( install / uninstall )'), - (19, 'tool', 'Use tool plugins'), - (19, 'report', 'Use report plugins'), - (19, 'configure', 'Configure plugins'), - (20, 'delete_public_lists', 'Delete public lists') +INSERT INTO permissions (module, code, description) VALUES + ( 'superlibrarian', 'superlibrarian', 'Access to all librarian functions'), + ( 'circulate', 'circulate_remaining_permissions', 'Remaining circulation permissions'), + ( 'circulate', 'override_renewals', 'Override blocked renewals'), + ( 'circulate', 'overdues_report', 'Execute overdue items report'), + ( 'circulate', 'force_checkout', 'Force checkout if a limitation exists'), + ( 'circulate', 'manage_restrictions', 'Manage restrictions for accounts'), + ( 'catalogue', 'staff_login', 'Allow staff login.'), + ( 'parameters', 'parameters_remaining_permissions', 'Remaining system parameters permissions'), + ( 'parameters', 'manage_circ_rules', 'manage circulation rules'), + ( 'borrowers', 'view_borrowers', 'Show borrower details and search for borrowers.'), + ( 'permissions', 'set_permissions', 'Set user permissions'), + ( 'reserveforothers','place_holds', 'Place holds for patrons'), + ( 'reserveforothers','modify_holds_priority', 'Modify holds priority'), + ( 'editcatalogue', 'edit_catalogue', 'Edit catalog (Modify bibliographic/holdings data)'), + ( 'editcatalogue', 'fast_cataloging', 'Fast cataloging'), + ( 'editcatalogue', 'edit_items', 'Edit items'), + ( 'editcatalogue', 'edit_items_restricted', 'Limit item modification to subfields defined in the SubfieldsToAllowForRestrictedEditing preference (please note that edit_item is still required)'), + ( 'editcatalogue', 'delete_all_items', 'Delete all items at once'), + ( 'updatecharges', 'writeoff', 'Write off fines and fees'), + ( 'updatecharges', 'remaining_permissions', 'Remaining permissions for managing fines and fees'), + ( 'acquisition', 'vendors_manage', 'Manage vendors'), + ( 'acquisition', 'contracts_manage', 'Manage contracts'), + ( 'acquisition', 'period_manage', 'Manage periods'), + ( 'acquisition', 'budget_manage', 'Manage budgets'), + ( 'acquisition', 'budget_modify', 'Modify budget (can''t create lines, but can modify existing ones)'), + ( 'acquisition', 'planning_manage', 'Manage budget plannings'), + ( 'acquisition', 'order_manage', 'Manage orders & basket'), + ( 'acquisition', 'order_manage_all', 'Manage all orders and baskets, regardless of restrictions on them'), + ( 'acquisition', 'group_manage', 'Manage orders & basketgroups'), + ( 'acquisition', 'order_receive', 'Manage orders & basket'), + ( 'acquisition', 'budget_add_del', 'Add and delete budgets (but can''t modify budgets)'), + ( 'acquisition', 'budget_manage_all', 'Manage all budgets'), + ( 'management', 'management', 'Set library management parameters (deprecated)'), + ( 'tools', 'edit_news', 'Write news for the OPAC and staff interfaces'), + ( 'tools', 'label_creator', 'Create printable labels and barcodes from catalog and patron data'), + ( 'tools', 'edit_calendar', 'Define days when the library is closed'), + ( 'tools', 'moderate_comments', 'Moderate patron comments'), + ( 'tools', 'edit_notices', 'Define notices'), + ( 'tools', 'edit_notice_status_triggers', 'Set notice/status triggers for overdue items'), + ( 'tools', 'edit_quotes', 'Edit quotes for quote-of-the-day feature'), + ( 'tools', 'view_system_logs', 'Browse the system logs'), + ( 'tools', 'inventory', 'Perform inventory (stocktaking) of your catalog'), + ( 'tools', 'stage_marc_import', 'Stage MARC records into the reservoir'), + ( 'tools', 'manage_staged_marc', 'Managed staged MARC records, including completing and reversing imports'), + ( 'tools', 'export_catalog', 'Export bibliographic and holdings data'), + ( 'tools', 'import_patrons', 'Import patron data'), + ( 'tools', 'edit_patrons', 'Perform batch modification of patrons'), + ( 'tools', 'delete_anonymize_patrons', 'Delete old borrowers and anonymize circulation history (deletes borrower reading history)'), + ( 'tools', 'batch_upload_patron_images', 'Upload patron images in a batch or one at a time'), + ( 'tools', 'schedule_tasks', 'Schedule tasks to run'), + ( 'tools', 'items_batchmod', 'Perform batch modification of items'), + ( 'tools', 'items_batchmod_restricted', 'Limit batch item modification to subfields defined in the SubfieldsToAllowForRestrictedBatchmod preference (please note that items_batchmod is still required)'), + ( 'tools', 'items_batchdel', 'Perform batch deletion of items'), + ( 'tools', 'manage_csv_profiles', 'Manage CSV export profiles'), + ( 'tools', 'moderate_tags', 'Moderate patron tags'), + ( 'tools', 'rotating_collections', 'Manage rotating collections'), + ( 'tools', 'upload_local_cover_images', 'Upload local cover images'), + ( 'tools', 'manage_patron_lists', 'Add, edit and delete patron lists and their contents'), + ( 'tools', 'records_batchmod', 'Perform batch modification of records (biblios or authorities)'), + ( 'tools', 'marc_modification_templates', 'Manage marc modification templates'), + ( 'tools', 'records_batchdel', 'Perform batch deletion of records (bibliographic or authority)'), + ( 'editauthorities', 'edit_authorities', 'Edit authorities'), + ( 'serials', 'check_expiration', 'Check the expiration of a serial'), + ( 'serials', 'claim_serials', 'Claim missing serials'), + ( 'serials', 'create_subscription', 'Create a new subscription'), + ( 'serials', 'delete_subscription', 'Delete an existing subscription'), + ( 'serials', 'edit_subscription', 'Edit an existing subscription'), + ( 'serials', 'receive_serials', 'Serials receiving'), + ( 'serials', 'renew_subscription', 'Renew a subscription'), + ( 'serials', 'routing', 'Routing'), + ( 'serials', 'superserials', 'Manage subscriptions from any branch (only applies when IndependentBranches is used)'), + ( 'reports', 'execute_reports', 'Execute SQL reports'), + ( 'reports', 'create_reports', 'Create SQL reports'), + ( 'staffaccess', 'staff_access_permissions', 'Allow staff members to modify permissions for other staff members'), + ( 'coursereserves', 'manage_courses', 'Add, edit and delete courses'), + ( 'coursereserves', 'add_reserves', 'Add course reserves'), + ( 'coursereserves', 'delete_reserves', 'Remove course reserves'), + ( 'plugins', 'manage', 'Manage plugins ( install / uninstall )'), + ( 'plugins', 'tool', 'Use tool plugins'), + ( 'plugins', 'report', 'Use report plugins'), + ( 'plugins', 'configure', 'Configure plugins'), + ( 'lists', 'delete_public_lists', 'Delete public lists') ; --- a/installer/data/mysql/kohastructure.sql +++ a/installer/data/mysql/kohastructure.sql @@ -249,7 +249,6 @@ CREATE TABLE `borrowers` ( -- this table includes information about your patrons `ethnotes` varchar(255) default NULL, -- unused in Koha `sex` varchar(1) default NULL, -- patron/borrower's gender `password` varchar(60) default NULL, -- patron/borrower's encrypted password - `flags` int(11) default NULL, -- will include a number associated with the staff member's permissions `userid` varchar(75) default NULL, -- patron/borrower's opac and/or staff client log in `opacnote` mediumtext, -- a note on the patron/borrower's account that is visible in the OPAC and staff client `contactnote` varchar(255) default NULL, -- a note related to the patron/borrower's alternate address @@ -879,7 +878,6 @@ CREATE TABLE `deletedborrowers` ( -- stores data related to the patrons/borrower `ethnotes` varchar(255) default NULL, -- unused in Koha `sex` varchar(1) default NULL, -- patron/borrower's gender `password` varchar(30) default NULL, -- patron/borrower's encrypted password - `flags` int(11) default NULL, -- will include a number associated with the staff member's permissions `userid` varchar(30) default NULL, -- patron/borrower's opac and/or staff client log in `opacnote` mediumtext, -- a note on the patron/borrower's account that is visible in the OPAC and staff client `contactnote` varchar(255) default NULL, -- a note related to the patron/borrower's alternate address @@ -2292,19 +2290,6 @@ CREATE TABLE `tags_index` ( -- a weighted list of all tags and where they are us ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- --- Table structure for table `userflags` --- - -DROP TABLE IF EXISTS `userflags`; -CREATE TABLE `userflags` ( - `bit` int(11) NOT NULL default 0, - `flag` varchar(30) default NULL, - `flagdesc` varchar(255) default NULL, - `defaulton` int(11) default NULL, - PRIMARY KEY (`bit`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - --- -- Table structure for table `virtualshelves` -- @@ -2485,20 +2470,6 @@ CREATE TABLE language_script_mapping ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- --- Table structure for table `permissions` --- - -DROP TABLE IF EXISTS `permissions`; -CREATE TABLE `permissions` ( - `module_bit` int(11) NOT NULL DEFAULT 0, - `code` varchar(64) DEFAULT NULL, - `description` varchar(255) DEFAULT NULL, - PRIMARY KEY (`module_bit`, `code`), - CONSTRAINT `permissions_ibfk_1` FOREIGN KEY (`module_bit`) REFERENCES `userflags` (`bit`) - ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - --- -- Table structure for table `serialitems` -- @@ -2513,21 +2484,6 @@ CREATE TABLE `serialitems` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- --- Table structure for table `user_permissions` --- - -DROP TABLE IF EXISTS `user_permissions`; -CREATE TABLE `user_permissions` ( - `borrowernumber` int(11) NOT NULL DEFAULT 0, - `module_bit` int(11) NOT NULL DEFAULT 0, - `code` varchar(64) DEFAULT NULL, - CONSTRAINT `user_permissions_ibfk_1` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) - ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `user_permissions_ibfk_2` FOREIGN KEY (`module_bit`, `code`) REFERENCES `permissions` (`module_bit`, `code`) - ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - --- -- Table structure for table `tmp_holdsqueue` -- @@ -3339,7 +3295,6 @@ CREATE TABLE IF NOT EXISTS `borrower_modifications` ( `ethnotes` varchar(255) DEFAULT NULL, `sex` varchar(1) DEFAULT NULL, `password` varchar(30) DEFAULT NULL, - `flags` int(11) DEFAULT NULL, `userid` varchar(75) DEFAULT NULL, `opacnote` mediumtext, `contactnote` varchar(255) DEFAULT NULL, @@ -3362,6 +3317,55 @@ CREATE TABLE IF NOT EXISTS `borrower_modifications` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- +-- Table structure for table permissions +-- + +DROP TABLE IF EXISTS permissions; +CREATE TABLE permissions ( + permission_id int(11) NOT NULL auto_increment, + module varchar(32) NOT NULL, + code varchar(64) NOT NULL, + description varchar(255) DEFAULT NULL, + PRIMARY KEY (permission_id), + UNIQUE KEY (code), + CONSTRAINT permissions_to_modules_ibfk1 FOREIGN KEY (module) REFERENCES permission_modules (module) + ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- +-- Table structure for table permission_modules +-- + +DROP TABLE IF EXISTS permission_modules; +CREATE TABLE permission_modules ( + permission_module_id int(11) NOT NULL auto_increment, + module varchar(32) NOT NULL, + description varchar(255) DEFAULT NULL, + PRIMARY KEY (permission_module_id), + UNIQUE KEY (module) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- +-- Table structure for table borrower_permissions +-- + +DROP TABLE IF EXISTS borrower_permissions; +CREATE TABLE borrower_permissions ( + borrower_permission_id int(11) NOT NULL auto_increment, + borrowernumber int(11) NOT NULL, + permission_module_id int(11) NOT NULL, + permission_id int(11) NOT NULL, + PRIMARY KEY (borrower_permission_id), + UNIQUE KEY (borrowernumber, permission_module_id, permission_id), + CONSTRAINT borrower_permissions_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT borrower_permissions_ibfk_2 FOREIGN KEY (permission_id) REFERENCES permissions (permission_id) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT borrower_permissions_ibfk_3 FOREIGN KEY (permission_module_id) REFERENCES permission_modules (permission_module_id) + ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- -- Table structure for table linktracker -- This stores clicks to external links -- --- a/installer/data/mysql/updatedatabase.pl +++ a/installer/data/mysql/updatedatabase.pl @@ -7823,6 +7823,119 @@ if ( CheckVersion($DBversion) ) { SetVersion($DBversion); } +$DBversion = "3.21.00.XXX"; +if ( CheckVersion($DBversion) ) { + my $upgradeDone; + my $permissionModule = $dbh->selectrow_hashref("SELECT * FROM permission_modules LIMIT 1"); + if ($permissionModule) { + print "Upgrade to $DBversion ALREADY DONE?!? (Bug 14540 - Move member-flags.pl to PermissionsManager to better manage permissions for testing.)\n"; + $upgradeDone = 1; + } + unless ($upgradeDone) { + ##CREATE new TABLEs + ##CREATing instead of ALTERing existing tables because this way the changes are more easy to understand. + $dbh->do("CREATE TABLE permission_modules ( + permission_module_id int(11) NOT NULL auto_increment, + module varchar(32) NOT NULL, + description varchar(255) DEFAULT NULL, + PRIMARY KEY (permission_module_id), + UNIQUE KEY (module) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + $dbh->do("INSERT INTO permission_modules (permission_module_id, module, description) SELECT bit, flag, flagdesc FROM userflags WHERE bit != 0;"); #superlibrarian causes primary key conflict + $dbh->do("INSERT INTO permission_modules (permission_module_id, module, description) SELECT 21, flag, flagdesc FROM userflags WHERE bit = 0;"); #So add him by himself. + + $dbh->do("ALTER TABLE permissions RENAME TO permissions_old"); + $dbh->do("CREATE TABLE permissions ( + permission_id int(11) NOT NULL auto_increment, + module varchar(32) NOT NULL, + code varchar(64) NOT NULL, + description varchar(255) DEFAULT NULL, + PRIMARY KEY (permission_id), + UNIQUE KEY (code), + CONSTRAINT permissions_to_modules_ibfk1 FOREIGN KEY (module) REFERENCES permission_modules (module) + ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + $dbh->do("INSERT INTO permissions (module, code, description) + SELECT userflags.flag, code, description FROM permissions_old + LEFT JOIN userflags ON permissions_old.module_bit = userflags.bit;"); + + $dbh->do("CREATE TABLE borrower_permissions ( + borrower_permission_id int(11) NOT NULL auto_increment, + borrowernumber int(11) NOT NULL, + permission_module_id int(11) NOT NULL, + permission_id int(11) NOT NULL, + PRIMARY KEY (borrower_permission_id), + UNIQUE KEY (borrowernumber, permission_module_id, permission_id), + CONSTRAINT borrower_permissions_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT borrower_permissions_ibfk_2 FOREIGN KEY (permission_id) REFERENCES permissions (permission_id) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT borrower_permissions_ibfk_3 FOREIGN KEY (permission_module_id) REFERENCES permission_modules (permission_module_id) + ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + $dbh->do("INSERT INTO borrower_permissions (borrowernumber, permission_module_id, permission_id) + SELECT user_permissions.borrowernumber, user_permissions.module_bit, permissions.permission_id + FROM user_permissions + LEFT JOIN permissions ON user_permissions.code = permissions.code + LEFT JOIN borrowers ON borrowers.borrowernumber = user_permissions.borrowernumber + WHERE borrowers.borrowernumber IS NOT NULL;"); #It is possible to have user_permissions dangling around not pointing to anything, so make sure we dont try to move nonexisting borrower permissions. + + ##Add subpermissions to the stand-alone modules (modules with no subpermissions) + $dbh->do("INSERT INTO permissions (module, code, description) VALUES ('superlibrarian', 'superlibrarian', 'Access to all librarian functions.');"); + $dbh->do("INSERT INTO permissions (module, code, description) VALUES ('catalogue', 'staff_login', 'Allow staff login.');"); + $dbh->do("INSERT INTO permissions (module, code, description) VALUES ('borrowers', 'view_borrowers', 'Show borrower details and search for borrowers.');"); + $dbh->do("INSERT INTO permissions (module, code, description) VALUES ('permissions', 'set_permissions', 'Set user permissions.');"); + $dbh->do("INSERT INTO permissions (module, code, description) VALUES ('management', 'management', 'Set library management parameters (deprecated).');"); + $dbh->do("INSERT INTO permissions (module, code, description) VALUES ('editauthorities', 'edit_authorities', 'Edit authorities.');"); + $dbh->do("INSERT INTO permissions (module, code, description) VALUES ('staffaccess', 'staff_access_permissions', 'Allow staff members to modify permissions for other staff members.');"); + + ##Create borrower_permissions to replace singular userflags from borrowers.flags. + use Koha::Borrowers; + my $sth = $dbh->prepare(" + INSERT INTO borrower_permissions (borrowernumber, permission_module_id, permission_id) + VALUES (?, + (SELECT permission_module_id FROM permission_modules WHERE module = ?), + (SELECT permission_id FROM permissions WHERE code = ?) + ); + "); + my @borrowers = Koha::Borrowers->search({}); + foreach my $b (@borrowers) { + next unless $b->flags; + if ( ( $b->flags & ( 2**0 ) ) ) { + $sth->execute($b->borrowernumber, 'superlibrarian', 'superlibrarian'); + } + if ( ( $b->flags & ( 2**2 ) ) ) { + $sth->execute($b->borrowernumber, 'catalogue', 'staff_login'); + } + if ( ( $b->flags & ( 2**4 ) ) ) { + $sth->execute($b->borrowernumber, 'borrowers', 'view_borrowers'); + } + if ( ( $b->flags & ( 2**5 ) ) ) { + $sth->execute($b->borrowernumber, 'permissions', 'set_permissions'); + } + if ( ( $b->flags & ( 2**12 ) ) ) { + $sth->execute($b->borrowernumber, 'management', 'management'); + } + if ( ( $b->flags & ( 2**14 ) ) ) { + $sth->execute($b->borrowernumber, 'editauthorities', 'edit_authorities'); + } + if ( ( $b->flags & ( 2**17 ) ) ) { + $sth->execute($b->borrowernumber, 'staffaccess', 'staff_access_permissions'); + } + } + + ##Cleanup redundant tables. + $dbh->do("DELETE FROM userflags"); #Cascades to other tables. + $dbh->do("DROP TABLE user_permissions"); + $dbh->do("DROP TABLE permissions_old"); + $dbh->do("DROP TABLE userflags"); + $dbh->do("ALTER TABLE borrowers DROP COLUMN flags"); + + print "Upgrade to $DBversion done (Bug 14540 - Move member-flags.pl to PermissionsManager to better manage permissions for testing.)\n"; + SetVersion($DBversion); + } +} + $DBversion = "3.15.00.002"; if(CheckVersion($DBversion)) { $dbh->do("ALTER TABLE deleteditems MODIFY materials text;"); --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member-flags.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member-flags.tt @@ -9,23 +9,23 @@ $("#permissionstree").treeview({animated: "fast", collapsed: true}); // Enforce Superlibrarian Privilege Mutual Exclusivity - if($('input[id="flag-0"]:checked').length){ + if($('input[id="flag-superlibrarian"]:checked').length){ if ($('input[name="flag"]:checked').length > 1){ alert('Inconsistency Detected!\n\nThe superlibrarian privilege is mutually exclusive of other privileges, as it includes them all.\n\nThis patron\'s privileges will now be reset to include only superlibrarian.'); } $('input[name="flag"]').each(function() { - if($(this).attr('id') != "flag-0"){ + if($(this).attr('id') != "flag-superlibrarian"){ $(this).attr('disabled', 'disabled'); $(this).removeAttr('checked', 'checked'); } }); } - $('input#flag-0').click(function() { - if($('input[id="flag-0"]:checked').length){ + $('input#flag-superlibrarian').click(function() { + if($('input[id="flag-superlibrarian"]:checked').length){ $('input[name="flag"]').each(function() { - if($(this).attr('id') != "flag-0"){ + if($(this).attr('id') != "flag-superlibrarian"){ $(this).attr('disabled', 'disabled'); $(this).removeAttr('checked', 'checked'); } @@ -121,9 +121,9 @@ [% FOREACH loo IN loop %] [% IF ( loo.expand ) %] -
  • +
  • [% ELSE %] -
  • +
  • [% END %] [% IF ( loo.checked ) %] --- a/members/member-flags.pl +++ a/members/member-flags.pl @@ -14,7 +14,9 @@ use C4::Context; use C4::Members; use C4::Branch; use C4::Members::Attributes qw(GetBorrowerAttributes); -#use C4::Acquisitions; +use Koha::Auth::PermissionManager; + +use Koha::Exception::BadParameter; use C4::Output; @@ -35,121 +37,43 @@ my ($template, $loggedinuser, $cookie) = get_template_and_user({ debug => 1, }); - +my $permissionManager = Koha::Auth::PermissionManager->new(); my %member2; $member2{'borrowernumber'}=$member; if ($input->param('newflags')) { my $dbh=C4::Context->dbh(); + #Cast CGI-params into a permissions HASH. my @perms = $input->param('flag'); - my %all_module_perms = (); my %sub_perms = (); foreach my $perm (@perms) { - if ($perm !~ /:/) { - $all_module_perms{$perm} = 1; + if ($perm eq 'superlibrarian') { + $sub_perms{superlibrarian}->{superlibrarian} = 1; + } + elsif ($perm !~ /:/) { + #DEPRECATED, GUI still sends the module flags here even though they have been removed from the DB. } else { my ($module, $sub_perm) = split /:/, $perm, 2; - push @{ $sub_perms{$module} }, $sub_perm; + $sub_perms{$module}->{$sub_perm} = 1; } } - # 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; - } - } - - $sth = $dbh->prepare("UPDATE borrowers SET flags=? WHERE borrowernumber=?"); - $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); - } - } - + $permissionManager->revokeAllPermissions($member); + $permissionManager->grantPermissions($member, \%sub_perms); + print $input->redirect("/cgi-bin/koha/members/moremember.pl?borrowernumber=$member"); } else { -# my ($bor,$flags,$accessflags)=GetMemberDetails($member,''); - my $flags = $bor->{'flags'}; - my $accessflags = $bor->{'authflags'}; - my $dbh=C4::Context->dbh(); - my $all_perms = get_all_subpermissions(); - my $user_perms = get_user_subpermissions($bor->{'userid'}); - my $sth=$dbh->prepare("SELECT bit,flag,flagdesc FROM userflags ORDER BY bit"); - $sth->execute; + my $all_perms = $permissionManager->listKohaPermissionsAsHASH(); + my $user_perms = $permissionManager->getBorrowerPermissions($member); + + $all_perms = markBorrowerGrantedPermissions($all_perms, $user_perms); + my @loop; - while (my ($bit, $flag, $flagdesc) = $sth->fetchrow) { - my $checked=''; - if ($accessflags->{$flag}) { - $checked= 1; - } - - my %row = ( bit => $bit, - flag => $flag, - checked => $checked, - flagdesc => $flagdesc ); - - 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, - description => $all_perms->{$flag}->{$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, - description => $all_perms->{$flag}->{$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, - description => $all_perms->{$flag}->{$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; + #Make sure the superlibrarian module is always on top. + push @loop, preparePermissionModuleForDisplay($all_perms, 'superlibrarian'); + foreach my $module (sort(keys(%$all_perms))) { + push @loop, preparePermissionModuleForDisplay($all_perms, $module) unless $module eq 'superlibrarian'; } if ( $bor->{'category_type'} eq 'C') { @@ -172,8 +96,8 @@ if (C4::Context->preference('ExtendedPatronAttributes')) { } # Computes full borrower address -my $roadtype = C4::Koha::GetAuthorisedValueByCode( 'ROADTYPE', $bor->{streettype} ); -my $address = $bor->{'streetnumber'} . " $roadtype " . $bor->{'address'}; +my $roadtype = C4::Koha::GetAuthorisedValueByCode( 'ROADTYPE', $bor->{streettype} ) || ''; +my $address = ($bor->{'streetnumber'} || '') . " $roadtype " . ($bor->{'address'} || ''); $template->param( borrowernumber => $bor->{'borrowernumber'}, @@ -206,3 +130,75 @@ $template->param( output_html_with_http_headers $input, $cookie, $template->output; } + +=head markBorrowerGrantedPermissions + +Adds a 'checked'-value for all subpermissions in the all-Koha-Permissions-list +that the current borrower has been granted. +@PARAM1 HASHRef of all Koha permissions and modules. +@PARAM1 ARRAYRef of all the granted Koha::Auth::BorrowerPermission-objects. +@RETURNS @PARAM1, slightly checked. +=cut + +sub markBorrowerGrantedPermissions { + my ($all_perms, $user_perms) = @_; + + foreach my $borrowerPermission (@$user_perms) { + my $module = $borrowerPermission->getPermissionModule->module; + my $code = $borrowerPermission->getPermission->code; + $all_perms->{$module}->{permissions}->{$code}->{checked} = 1; + } + return $all_perms; +} + +=head checkIfAllModulePermissionsGranted + +@RETURNS Boolean, 1 if all permissions granted. +=cut + +sub checkIfAllModulePermissionsGranted { + my ($moduleHash) = @_; + foreach my $code (keys(%{$moduleHash->{permissions}})) { + unless ($moduleHash->{permissions}->{$code}->{checked}) { + return 0; + } + } + return 1; +} + +sub preparePermissionModuleForDisplay { + my ($all_perms, $module) = @_; + + my $moduleHash = $all_perms->{$module}; + my $checked = checkIfAllModulePermissionsGranted($moduleHash); + + my %row = ( + bit => $module, + flag => $module, + checked => $checked, + flagdesc => $moduleHash->{description} ); + + my @sub_perm_loop = (); + my $expand_parent = 0; + + if ($module ne 'superlibrarian') { + foreach my $sub_perm (sort keys %{ $all_perms->{$module}->{permissions} }) { + my $sub_perm_checked = $all_perms->{$module}->{permissions}->{$sub_perm}->{checked}; + $expand_parent = 1 if $sub_perm_checked; + + push @sub_perm_loop, { + id => "${module}_$sub_perm", + perm => "$module:$sub_perm", + code => $sub_perm, + description => $all_perms->{$module}->{permissions}->{$sub_perm}->{description}, + checked => $sub_perm_checked || 0, + }; + } + + $row{expand} = $expand_parent; + if ($#sub_perm_loop > -1) { + $row{sub_perm_loop} = \@sub_perm_loop; + } + } + return \%row; +} --- a/misc/devel/interactiveWebDriverShell.pl +++ a/misc/devel/interactiveWebDriverShell.pl @@ -115,13 +115,13 @@ my $supportedPageObjects = { "members/moremember.pl" => { package => "t::lib::Page::Members::Moremember", urlEndpoint => "members/moremember.pl", - status => "not implemented", + status => "OK", params => ["borrowernumber"], }, "members/member-flags.pl" => { package => "t::lib::Page::Members::MemberFlags", urlEndpoint => "members/member-flags.pl", - status => "not implemented", + status => "OK", params => ["borrowernumber"], }, ################################################################################ @@ -132,6 +132,11 @@ my $supportedPageObjects = { urlEndpoint => "opac/opac-main.pl", status => "OK", }, + "opac/opac-search.pl" => + { package => "t::lib::Page::Opac::OpacSearch", + urlEndpoint => "opac/opac-search.pl", + status => "OK", + }, }; ################################################################################ ########## END OF PAGE CONFIGURATIONS ########## --- a/opac/opac-user.pl +++ a/opac/opac-user.pl @@ -349,7 +349,7 @@ foreach my $res (@reserves) { $template->param( WAITING => \@waiting ); # current alert subscriptions -my $alerts = getalert($borrowernumber); +my $alerts = getalert($borrowernumber) if $borrowernumber; foreach ( @$alerts ) { $_->{ $_->{type} } = 1; $_->{relatedto} = findrelatedto( $_->{type}, $_->{externalid} ); --- a/t/db_dependent/Koha/Auth.t +++ a/t/db_dependent/Koha/Auth.t @@ -22,7 +22,10 @@ use Modern::Perl; use Test::More; use Try::Tiny; #Even Selenium::Remote::Driver uses Try::Tiny :) +use Koha::Auth::PermissionManager; + use t::lib::Page::Mainpage; +use t::lib::Page::Opac::OpacMain; use t::lib::TestObjects::BorrowerFactory; @@ -36,17 +39,36 @@ my $borrowers = $borrowerFactory->createTestGroup([ surname => 'Kivi', cardnumber => '1A01', branchcode => 'CPL', - flags => '1', #superlibrarian, not exactly a very good way of doing permission testing? userid => 'mini_admin', password => $password, }, + {firstname => 'Admin', + surname => 'Administrative', + cardnumber => 'maxi_admin', + branchcode => 'FPL', + userid => 'maxi_admin', + password => $password, + }, ], undef, $testContext); +my $permissionManager = Koha::Auth::PermissionManager->new(); +$permissionManager->grantPermission($borrowers->{'1A01'}, 'catalogue', 'staff_login'); +$permissionManager->grantPermission($borrowers->{'maxi_admin'}, 'superlibrarian', 'superlibrarian'); + ##Test context set, starting testing: eval { #run in a eval-block so we don't die without tearing down the test context - testPasswordLogin(); + my $mainpage = t::lib::Page::Mainpage->new(); + testPasswordLoginLogout($mainpage); + testSuperuserPasswordLoginLogout($mainpage); + testSuperlibrarianPasswordLoginLogout($mainpage); + $mainpage->quit(); + my $opacmain = t::lib::Page::Opac::OpacMain->new(); + testOpacPasswordLoginLogout($opacmain); + testSuperuserPasswordLoginLogout($opacmain); + testOpacSuperlibrarianPasswordLoginLogout($opacmain); + $opacmain->quit(); }; if ($@) { #Catch all leaking errors and gracefully terminate. warn $@; @@ -66,7 +88,30 @@ sub tearDown { ### STARTING TEST IMPLEMENTATIONS ### ###################################################### -sub testPasswordLogin { - my $mainpage = t::lib::Page::Mainpage->new(); - $mainpage->isPasswordLoginAvailable()->doPasswordLogin($borrowers->{'1A01'}->userid(), $password)->quit(); -} +sub testPasswordLoginLogout { + my ($mainpage) = @_; + $mainpage->isPasswordLoginAvailable()->doPasswordLogin($borrowers->{'1A01'}->userid(), $password) + ->isLoggedInBranchCode($borrowers->{'1A01'}->branchcode()) + ->doPasswordLogout(); +} +sub testOpacPasswordLoginLogout { + my ($mainpage) = @_; + $mainpage->isPasswordLoginAvailable()->doPasswordLogin($borrowers->{'1A01'}->userid(), $password) + ->doPasswordLogout(); +} +sub testSuperuserPasswordLoginLogout { + my ($mainpage) = @_; + $mainpage->isPasswordLoginAvailable()->doPasswordLogin(C4::Context->config('user'), C4::Context->config('pass')) + ->doPasswordLogout(); +} +sub testSuperlibrarianPasswordLoginLogout { + my ($mainpage) = @_; + $mainpage->isPasswordLoginAvailable()->doPasswordLogin($borrowers->{'maxi_admin'}->userid(), $password) + ->isLoggedInBranchCode($borrowers->{'maxi_admin'}->branchcode()) + ->doPasswordLogout(); +} +sub testOpacSuperlibrarianPasswordLoginLogout { + my ($mainpage) = @_; + $mainpage->isPasswordLoginAvailable()->doPasswordLogin($borrowers->{'maxi_admin'}->userid(), $password) + ->doPasswordLogout(); +} --- a/t/db_dependent/Koha/Auth/BorrowerPermission.t +++ a/t/db_dependent/Koha/Auth/BorrowerPermission.t @@ -0,0 +1,102 @@ +#!/usr/bin/perl + +# Copyright 2015 Vaara-kirjastot +# +# 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 Test::More; + +use Koha::Auth::BorrowerPermission; +use Koha::Auth::BorrowerPermissions; + +use t::lib::TestObjects::ObjectFactory; +use t::lib::TestObjects::BorrowerFactory; + +##Setting up the test context +my $testContext = {}; + +my $borrowerFactory = t::lib::TestObjects::BorrowerFactory->new(); +my $borrowers = $borrowerFactory->createTestGroup([ + {firstname => 'Olli-Antti', + surname => 'Kivi', + cardnumber => '1A01', + branchcode => 'CPL', + }, + {firstname => 'Alli-Ontti', + surname => 'Ivik', + cardnumber => '1A02', + branchcode => 'CPL', + }, + ], undef, $testContext); + +##Test context set, starting testing: +eval { #run in a eval-block so we don't die without tearing down the test context + ##Basic id-based creation. + my $borrowerPermissionById = Koha::Auth::BorrowerPermission->new({borrowernumber => $borrowers->{'1A01'}->borrowernumber, permission_module_id => 1, permission_id => 1}); + $borrowerPermissionById->store(); + my @borrowerPermissionById = Koha::Auth::BorrowerPermissions->search({borrowernumber => $borrowers->{'1A01'}->borrowernumber}); + is(scalar(@borrowerPermissionById), 1, "BorrowerPermissions, id-based creation:> Borrower has only one permission"); + is($borrowerPermissionById[0]->permission_module_id, 1, "BorrowerPermissions, id-based creation:> Same permission_module_id"); + is($borrowerPermissionById[0]->permission_id, 1, "BorrowerPermissions, id-based creation:> Same permission_id"); + + ##Basic name-based creation. + my $borrowerPermissionByName = Koha::Auth::BorrowerPermission->new({borrowernumber => $borrowers->{'1A02'}->borrowernumber, permissionModule => 'circulate', permission => 'manage_restrictions'}); + $borrowerPermissionByName->store(); + my @borrowerPermissionByName = Koha::Auth::BorrowerPermissions->search({borrowernumber => $borrowers->{'1A02'}->borrowernumber}); + is(scalar(@borrowerPermissionByName), 1, "BorrowerPermissions, name-based creation:> Borrower has only one permission"); + is($borrowerPermissionByName[0]->getPermissionModule->module, 'circulate', "BorrowerPermissions, name-based creation:> Same permission_module"); + is($borrowerPermissionByName[0]->getPermission->code, 'manage_restrictions', "BorrowerPermissions, name-based creation:> Same permission"); + + ##Testing setter/getter for Borrower + my $borrower1A01 = $borrowerPermissionById->getBorrower(); + is($borrower1A01->cardnumber, "1A01", "BorrowerPermissions, setter/getter:> getBorrower() 1A01"); + my $borrower1A02 = $borrowerPermissionByName->getBorrower(); + is($borrower1A02->cardnumber, "1A02", "BorrowerPermissions, setter/getter:> getBorrower() 1A02"); + + $borrowerPermissionById->setBorrower($borrower1A02); + is($borrowerPermissionById->getBorrower()->cardnumber, "1A02", "BorrowerPermissions, setter/getter:> setBorrower() 1A02"); + $borrowerPermissionByName->setBorrower($borrower1A01); + is($borrowerPermissionByName->getBorrower()->cardnumber, "1A01", "BorrowerPermissions, setter/getter:> setBorrower() 1A01"); + + ##Testing getter for PermissionModule + my $permissionModule1 = $borrowerPermissionById->getPermissionModule(); + is($permissionModule1->permission_module_id, 1, "BorrowerPermissions, setter/getter:> getPermissionModule() 1"); + my $permissionModuleCirculate = $borrowerPermissionByName->getPermissionModule(); + is($permissionModuleCirculate->module, "circulate", "BorrowerPermissions, setter/getter:> getPermissionModule() circulate"); + + #Not testing setters because changing the module might not make any sense. + #Then we would need to make sure we dont end up with bad permissionModule->permission combinations. + + ##Testing getter for Permission + my $permission1 = $borrowerPermissionById->getPermission(); + is($permission1->permission_id, 1, "BorrowerPermissions, setter/getter:> getPermission() 1"); + my $permissionManage_restrictions = $borrowerPermissionByName->getPermission(); + is($permissionManage_restrictions->code, "manage_restrictions", "BorrowerPermissions, setter/getter:> getPermission() manage_restrictions"); +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +##All tests done, tear down test context +$borrowerFactory->tearDownTestContext($testContext); +done_testing; + +sub tearDown { + t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); +} --- a/t/db_dependent/Koha/Auth/PermissionManager.t +++ a/t/db_dependent/Koha/Auth/PermissionManager.t @@ -0,0 +1,221 @@ +#!/usr/bin/perl + +# Copyright 2015 Vaara-kirjastot +# +# 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 Test::More; +use Try::Tiny; +use Scalar::Util qw(blessed); + +use Koha::Auth::PermissionManager; + +use t::lib::TestObjects::ObjectFactory; +use t::lib::TestObjects::BorrowerFactory; + + +##Setting up the test context +my $testContext = {}; + +my $borrowerFactory = t::lib::TestObjects::BorrowerFactory->new(); +my $borrowers = $borrowerFactory->createTestGroup([ + {firstname => 'Olli-Antti', + surname => 'Kivi', + cardnumber => '1A01', + branchcode => 'CPL', + }, + {firstname => 'Alli-Ontti', + surname => 'Ivik', + cardnumber => '1A02', + branchcode => 'CPL', + }, + ], undef, $testContext); + +##Test context set, starting testing: +eval { #run in a eval-block so we don't die without tearing down the test context + my $permissionManager = Koha::Auth::PermissionManager->new(); + my ($permissionModule, $permission, $failureCaughtFlag, $permissionsList); + + ##Test getBorrowerPermissions + $permissionManager->grantPermission($borrowers->{'1A01'}, 'circulate', 'force_checkout'); + $permissionManager->grantPermission($borrowers->{'1A01'}, 'circulate', 'manage_restrictions'); + $permissionsList = $permissionManager->getBorrowerPermissions($borrowers->{'1A01'}); + is($permissionsList->[0]->getPermission->code, 'force_checkout', "PermissionManager, getBorrowerPermissions:> Check 1."); + is($permissionsList->[1]->getPermission->code, 'manage_restrictions', "PermissionManager, getBorrowerPermissions:> Check 2."); + $permissionManager->revokePermission($borrowers->{'1A01'}, 'circulate', 'force_checkout'); + $permissionManager->revokePermission($borrowers->{'1A01'}, 'circulate', 'manage_restrictions'); + + ##Test grantPermissions && revokeAllPermissions + $permissionManager->grantPermissions($borrowers->{'1A01'}, + { borrowers => 'view_borrowers', + reserveforothers => ['place_holds'], + tools => ['edit_news', 'edit_notices'], + acquisition => { + budget_add_del => 1, + budget_modify => 1, + }, + }); + $permissionManager->hasPermission($borrowers->{'1A01'}, 'borrowers', 'view_borrowers'); + $permissionManager->hasPermission($borrowers->{'1A01'}, 'tools', 'edit_notices'); + $permissionManager->hasPermission($borrowers->{'1A01'}, 'acquisition', 'budget_modify'); + $permissionsList = $permissionManager->getBorrowerPermissions($borrowers->{'1A01'}); + is(scalar(@$permissionsList), 6, "PermissionManager, grantPermissions:> Permissions as HASH, ARRAY and Scalar."); + + $permissionManager->revokeAllPermissions($borrowers->{'1A01'}); + $permissionsList = $permissionManager->getBorrowerPermissions($borrowers->{'1A01'}); + is(scalar(@$permissionsList), 0, "PermissionManager, revokeAllPermissions:> No permissions left."); + + ##Test listKohaPermissionsAsHASH + my $listedPermissions = $permissionManager->listKohaPermissionsAsHASH(); + ok(ref($listedPermissions->{circulate}->{permissions}->{force_checkout}) eq 'HASH', "PermissionManager, listKohaPermissionsAsHASH:> Check 1."); + ok(ref($listedPermissions->{editcatalogue}->{permissions}->{edit_catalogue}) eq 'HASH', "PermissionManager, listKohaPermissionsAsHASH:> Check 2."); + ok(defined($listedPermissions->{reports}->{permissions}->{create_reports}->{description}), "PermissionManager, listKohaPermissionsAsHASH:> Check 3."); + ok(defined($listedPermissions->{permissions}->{description}), "PermissionManager, listKohaPermissionsAsHASH:> Check 4."); + + + + ### TESTING WITH unique keys, instead of the recommended Koha::Objects. ### + #Arguably this makes for more clear tests cases :) + ##Add/get PermissionModule + $permissionModule = $permissionManager->addPermissionModule({module => 'test', description => 'Just testing this module.'}); + is($permissionModule->module, "test", "PermissionManager from names, add/getPermissionModule:> Module added."); + $permissionModule = $permissionManager->getPermissionModule('test'); + is($permissionModule->module, "test", "PermissionManager from names, add/getPermissionModule:> Module got."); + + ##Add/get Permission + $permission = $permissionManager->addPermission({module => 'test', code => 'testperm', description => 'Just testing this permission.'}); + is($permission->code, "testperm", "PermissionManager from names, add/getPermission:> Permission added."); + $permission = $permissionManager->getPermission('testperm'); + is($permission->code, "testperm", "PermissionManager from names, add/getPermission:> Permission got."); + + ##Grant permission + $permissionManager->grantPermission($borrowers->{'1A01'}, 'test', 'testperm'); + ok($permissionManager->hasPermission($borrowers->{'1A01'}, 'test', 'testperm'), "PermissionManager from names, grant/hasPermission:> Borrower granted permission."); + + ##hasPermission with wildcard + ok($permissionManager->hasPermission($borrowers->{'1A01'}, 'test', '*'), "PermissionManager from names, hasPermission:> Wildcard permission."); + + ##hasPermissions with wildcard + ok($permissionManager->hasPermissions($borrowers->{'1A01'}, {test => ['*']}), "PermissionManager from names, hasPermission:> Wildcard permissions from array."); + + ##Revoke permission + $permissionManager->revokePermission($borrowers->{'1A01'}, 'test', 'testperm'); + $failureCaughtFlag = 0; + try { + $permissionManager->hasPermission($borrowers->{'1A01'}, 'test', 'testperm'); + } catch { + if (blessed($_) && $_->isa('Koha::Exception::NoPermission')) { + $failureCaughtFlag = 1; + } + else { + die $_; #Somekind of another problem arised and rethrow it. + } + }; + ok($failureCaughtFlag, "PermissionManager from names, revoke/hasPermission:> Borrower revoked permission."); + + ##Delete permissions and modules we just made. When we delete the module first, the permissions is ON CASCADE DELETEd + $permissionManager->delPermissionModule('test'); + $permissionModule = $permissionManager->getPermissionModule('test'); + ok(not(defined($permissionModule)), "PermissionManager from names, delPermissionModule:> Module deleted."); + + $failureCaughtFlag = 0; + try { + #This subpermission is now deleted due to cascading delete of the parent permissionModule + #We catch the exception gracefully and report test success + $permissionManager->delPermission('testperm'); + } catch { + if (blessed($_) && $_->isa('Koha::Exception::UnknownObject')) { + $failureCaughtFlag = 1; + } + else { + die $_; #Somekind of another problem arised and rethrow it. + } + }; + ok($failureCaughtFlag, "PermissionManager from names, delPermission:> Permission already deleted, exception caught."); + $permission = $permissionManager->getPermission('testperm'); + ok(not(defined($permission)), "PermissionManager from names, delPermission:> Permission deleted."); + + + + ### TESTING WITH Koha::Object parameters instead. ### + ##Add/get PermissionModule + $permissionModule = $permissionManager->addPermissionModule({module => 'test', description => 'Just testing this module.'}); + is($permissionModule->module, "test", "PermissionManager from objects, add/getPermissionModule:> Module added."); + $permissionModule = $permissionManager->getPermissionModule($permissionModule); + is($permissionModule->module, "test", "PermissionManager from objects, add/getPermissionModule:> Module got."); + + ##Add/get Permission + $permission = $permissionManager->addPermission({module => 'test', code => 'testperm', description => 'Just testing this permission.'}); + is($permission->code, "testperm", "PermissionManager from objects, add/getPermission:> Permission added."); + $permission = $permissionManager->getPermission($permission); + is($permission->code, "testperm", "PermissionManager from objects, add/getPermission:> Permission got."); + + ##Grant permission + $permissionManager->grantPermission($borrowers->{'1A01'}, $permissionModule, $permission); + ok($permissionManager->hasPermission($borrowers->{'1A01'}, $permissionModule, $permission), "PermissionManager from objects, grant/hasPermission:> Borrower granted permission."); + + ##hasPermission with wildcard + ok($permissionManager->hasPermission($borrowers->{'1A01'}, $permissionModule, '*'), "PermissionManager from objects, hasPermission:> Wildcard permission."); + + ##hasPermissions with wildcard, we quite cannot use a blessed Object as a HASH key + #ok($permissionManager->hasPermissions($borrowers->{'1A01'}, {$permissionModule->module() => ['*']}), "PermissionManager from objects, hasPermission:> Wildcard permissions from array."); + + ##Revoke permission + $permissionManager->revokePermission($borrowers->{'1A01'}, $permissionModule, $permission); + $failureCaughtFlag = 0; + try { + $permissionManager->hasPermission($borrowers->{'1A01'}, $permissionModule, $permission); + } catch { + if (blessed($_) && $_->isa('Koha::Exception::NoPermission')) { + $failureCaughtFlag = 1; + } + else { + die $_; #Somekind of another problem arised and rethrow it. + } + }; + ok($failureCaughtFlag, "PermissionManager from objects, revoke/hasPermission:> Borrower revoked permission."); + + ##Delete permissions and modules we just made + $permissionManager->delPermission($permission); + $permission = $permissionManager->getPermission('testperm'); + ok(not(defined($permission)), "PermissionManager from objects, delPermission:> Permission deleted."); + + $permissionManager->delPermissionModule($permissionModule); + $permissionModule = $permissionManager->getPermissionModule('test'); + ok(not(defined($permissionModule)), "PermissionManager from objects, delPermissionModule:> Module deleted."); + + + + ##Testing superlibrarian permission + $permissionManager->revokeAllPermissions($borrowers->{'1A01'}); + $permissionManager->grantPermission($borrowers->{'1A01'}, 'superlibrarian', 'superlibrarian'); + ok($permissionManager->hasPermission($borrowers->{'1A01'}, 'staffaccess', 'staff_access_permissions'), "PermissionManager, superuser permission:> Superuser has all permissions 1."); + ok($permissionManager->hasPermission($borrowers->{'1A01'}, 'tools', 'batch_upload_patron_images'), "PermissionManager, superuser permission:> Superuser has all permissions 2."); +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +##All tests done, tear down test context +tearDown(); +done_testing; + +sub tearDown { + t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); +} --- a/t/db_dependent/Members/member-flags.t +++ a/t/db_dependent/Members/member-flags.t @@ -0,0 +1,100 @@ +#!/usr/bin/env perl + +# Copyright 2015 Open Source Freedom Fighters +# +# 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 Test::More; +use Try::Tiny; #Even Selenium::Remote::Driver uses Try::Tiny :) +use Scalar::Util qw(blessed); + +use t::lib::Page::Members::MemberFlags; +use t::lib::TestObjects::BorrowerFactory; +use Koha::Auth::PermissionManager; + + +##Setting up the test context +my $testContext = {}; + +my $password = '1234'; +my $borrowerFactory = t::lib::TestObjects::BorrowerFactory->new(); +my $borrowers = $borrowerFactory->createTestGroup([ + {firstname => 'Olli-Antti', + surname => 'Kivi', + cardnumber => '1A01', + branchcode => 'CPL', + userid => 'mini_admin', + password => $password, + }, + ], undef, $testContext); + +##Test context set, starting testing: +eval { #run in a eval-block so we don't die without tearing down the test context + + testGrantRevokePermissions(); + +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +##All tests done, tear down test context +tearDown(); +done_testing; + +sub tearDown { + t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); +} + +sub testGrantRevokePermissions { + my $permissionManager = Koha::Auth::PermissionManager->new(); + $permissionManager->grantPermissions($borrowers->{'1A01'}, {permissions => 'set_permissions', + catalogue => 'staff_login', + staffaccess => 'staff_access_permissions', + circulate => 'override_renewals', + borrowers => 'view_borrowers', + }); + + my $memberflags = t::lib::Page::Members::MemberFlags->new({borrowernumber => $borrowers->{'1A01'}->borrowernumber}); + + $memberflags->isPasswordLoginAvailable()->doPasswordLogin($borrowers->{'1A01'}->userid, $password) + ->togglePermission('editcatalogue', 'delete_all_items') #Add this + ->togglePermission('editcatalogue', 'edit_items') #Add this + ->togglePermission('circulate', 'override_renewals') #Remove this permission + ->submitPermissionTree(); + + ok($permissionManager->hasPermissions($borrowers->{'1A01'},{editcatalogue => ['delete_all_items', 'edit_items']}), + "member-flags.pl:> Granting new permissions succeeded."); + + my $failureCaughtFlag = 0; + try { + $permissionManager->hasPermission($borrowers->{'1A01'}, 'circulate', 'override_renewals'); + } catch { + if (blessed($_) && $_->isa('Koha::Exception::NoPermission')) { + $failureCaughtFlag = 1; + } + else { + die $_; #Somekind of another problem arised and rethrow it. + } + }; + ok($failureCaughtFlag, "member-flags.pl:> Revoking permissions succeeded."); + + $permissionManager->revokeAllPermissions($borrowers->{'1A01'}); +} --- a/t/db_dependent/Opac/opac-search.t +++ a/t/db_dependent/Opac/opac-search.t @@ -0,0 +1,79 @@ +#!/usr/bin/env perl + +# Copyright 2015 Open Source Freedom Fighters +# +# 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 Test::More; + +use t::lib::Page::Opac::OpacSearch; +use t::lib::TestObjects::BorrowerFactory; + + +##Setting up the test context +my $testContext = {}; + +my $password = '1234'; +my $borrowerFactory = t::lib::TestObjects::BorrowerFactory->new(); +my $borrowers = $borrowerFactory->createTestGroup([ + {firstname => 'Olli-Antti', + surname => 'Kivi', + cardnumber => '1A01', + branchcode => 'CPL', + userid => 'mini_admin', + password => $password, + }, + ], undef, $testContext); + +##Test context set, starting testing: +eval { #run in a eval-block so we don't die without tearing down the test context + + my $opacsearch = t::lib::Page::Opac::OpacSearch->new(); + testAnonymousSearchHistory($opacsearch); + +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +##All tests done, tear down test context +tearDown(); +done_testing; + +sub tearDown { + t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); +} + +sub testAnonymousSearchHistory { + my $opacsearch = shift; + + $opacsearch->doSetSearchFieldTerm(1, 'Author', 'nengard')->doSearchSubmit()->navigateAdvancedSearch() + ->doSetSearchFieldTerm(1, 'Author', 'joubu')->doSearchSubmit()->navigateAdvancedSearch() + ->doSetSearchFieldTerm(1, 'Author', 'khall')->doSearchSubmit()->navigateHome() + ->doPasswordLogin($borrowers->{'1A01'}->userid, $password)->navigateAdvancedSearch() + ->doSetSearchFieldTerm(1, 'Author', 'magnuse')->doSearchSubmit()->navigateAdvancedSearch() + ->doSetSearchFieldTerm(1, 'Author', 'cait')->doSearchSubmit()->navigateSearchHistory() + ->testDoSearchHistoriesExist(['nengard', + 'joubu', + 'khall', + 'magnuse', + 'cait']) + ->quit(); +} --