From 70d33a82bb5d30c0455070e96cc5ad4fea545a9d Mon Sep 17 00:00:00 2001 From: Galen Charlton Date: Thu, 10 Apr 2008 11:37:43 -0500 Subject: [PATCH] granular permissions - C4::Auth Enhanced the permission-checking functions in C4::Auth (e.g., get_template_and_user, checkauth, check_api_auth, etc.). If the CheckSpecificUserPermissions syspref is OFF, the behavior of those APIs is unchanged. If CheckSpecificUserPermissions is ON, then the value of each key in the flagsrequired hash is considered during the permissions check: 1. if the value is 1, the staff user must have all privileges for the userflags flag (or module) referred to by the hash key, e.g., { flaqsrequired => { tools => 1 } } In terms of the database, this means that the corresponding bit must be set in borrowers.flags. 2. If the value is '*', the staff user must have at least one of the permissions for the userflag/module, but it doesn't matter which one. In terms of the database, this means ether that the corresponding bit must be set in borrowers.flags or that there at least is one row in user_permissions for the staff user and bit/module combination. 3. If the value is any other string, it must be a permissions code defined in the permissions table. The staff user must have that specific permission or have access to all functions of the module In terms of the database, this means ether that the corresponding bit must be set in borrowers.flags or that there is a matching row in user_permissions for the staff user, bit/module, subpermission code combination. In addition, get_template_and_user is modified so that the CAN_user_XXX variables that it sets also includes the subpermissions available (CAN_user_XXX_YYY, e.g., CAN_user_tools_import_patrons). The template variables for the specific permissions are set regardless of whether CheckSpecificUserPermissions is ON or OFF so that the templates don't have to test for that syspref explicitly. In addition, the meaning of CAN_user_XXX has changed slightly - CAN_user_tools, for example, is set to 1 in the template if the user has access to *any* of the tools functions. This was done to simply the logic for deciding whether to display a menu item in the staff interface are not. This does mean that when specific subpermissions are added to (say) the circulate module, each use of CAN_user_circulate will need to be examined to see if the intent is to allow the user to get at a circ menu or page or if the user really should be required to have all circulate functions. Signed-off-by: Joshua Ferraro --- C4/Auth.pm | 226 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 162 insertions(+), 64 deletions(-) diff --git a/C4/Auth.pm b/C4/Auth.pm index ba7f7dda11..ec0fe5966a 100755 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -41,7 +41,8 @@ BEGIN { $debug = $ENV{DEBUG} || 0 ; @ISA = qw(Exporter); @EXPORT = qw(&checkauth &get_template_and_user); - @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw); + @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions); + %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]); $ldap = C4::Context->config('useldapserver') || 0; if ($ldap) { require C4::Auth_with_ldap; # no import @@ -67,7 +68,7 @@ C4::Auth - Authenticates Koha users query => $query, type => "opac", authnotrequired => 1, - flagsrequired => {borrow => 1}, + flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' }, } ); @@ -97,7 +98,7 @@ C4::Auth - Authenticates Koha users query => $query, type => "opac", authnotrequired => 1, - flagsrequired => {borrow => 1}, + flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' }, } ); @@ -158,6 +159,8 @@ sub get_template_and_user { my @bordat; $bordat[0] = $borr; $template->param( "USER_INFO" => \@bordat ); + + my $all_perms = get_all_subpermissions(); my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow editcatalogue updatecharges management tools editauthorities serials reports); @@ -169,7 +172,7 @@ sub get_template_and_user { $template->param( CAN_user_catalogue => 1 ); $template->param( CAN_user_parameters => 1 ); $template->param( CAN_user_borrowers => 1 ); - $template->param( CAN_user_permission => 1 ); + $template->param( CAN_user_permissions => 1 ); $template->param( CAN_user_reserveforothers => 1 ); $template->param( CAN_user_borrow => 1 ); $template->param( CAN_user_editcatalogue => 1 ); @@ -181,68 +184,47 @@ sub get_template_and_user { $template->param( CAN_user_serials => 1 ); $template->param( CAN_user_reports => 1 ); $template->param( CAN_user_staffaccess => 1 ); + foreach my $module (keys %$all_perms) { + foreach my $subperm (keys %{ $all_perms->{$module} }) { + $template->param( "CAN_user_${module}_${subperm}" => 1 ); + } + } } - if ( $flags && $flags->{circulate} == 1 ) { - $template->param( CAN_user_circulate => 1 ); - } - - if ( $flags && $flags->{catalogue} == 1 ) { - $template->param( CAN_user_catalogue => 1 ); - } - - if ( $flags && $flags->{parameters} == 1 ) { - $template->param( CAN_user_parameters => 1 ); - $template->param( CAN_user_management => 1 ); - } - - if ( $flags && $flags->{borrowers} == 1 ) { - $template->param( CAN_user_borrowers => 1 ); - } - - if ( $flags && $flags->{permissions} == 1 ) { - $template->param( CAN_user_permission => 1 ); - } - - if ( $flags && $flags->{reserveforothers} == 1 ) { - $template->param( CAN_user_reserveforothers => 1 ); - } - - if ( $flags && $flags->{borrow} == 1 ) { - $template->param( CAN_user_borrow => 1 ); - } - - if ( $flags && $flags->{editcatalogue} == 1 ) { - $template->param( CAN_user_editcatalogue => 1 ); - } - - if ( $flags && $flags->{updatecharges} == 1 ) { - $template->param( CAN_user_updatecharges => 1 ); - } - - if ( $flags && $flags->{acquisition} == 1 ) { - $template->param( CAN_user_acquisition => 1 ); - } - - if ( $flags && $flags->{tools} == 1 ) { - $template->param( CAN_user_tools => 1 ); - } - - if ( $flags && $flags->{editauthorities} == 1 ) { - $template->param( CAN_user_editauthorities => 1 ); - } - - if ( $flags && $flags->{serials} == 1 ) { - $template->param( CAN_user_serials => 1 ); + if (C4::Context->preference('CheckSpecificUserPermissions')) { + if ( $flags ) { + foreach my $module (keys %$all_perms) { + if ( $flags->{$module} == 1) { + foreach my $subperm (keys %{ $all_perms->{$module} }) { + $template->param( "CAN_user_${module}_${subperm}" => 1 ); + } + } elsif ( ref($flags->{$module}) ) { + foreach my $subperm (keys %{ $flags->{$module} } ) { + $template->param( "CAN_user_${module}_${subperm}" => 1 ); + } + } + } + } + } else { + foreach my $module (keys %$all_perms) { + foreach my $subperm (keys %{ $all_perms->{$module} }) { + $template->param( "CAN_user_${module}_${subperm}" => 1 ); + } + } } - if ( $flags && $flags->{reports} == 1 ) { - $template->param( CAN_user_reports => 1 ); - } - if ( $flags && $flags->{staffaccess} == 1 ) { - $template->param( CAN_user_staffaccess => 1 ); + if ($flags) { + foreach my $module (keys %$flags) { + if ( $flags->{$module} == 1 or ref($flags->{$module}) ) { + $template->param( "CAN_user_$module" => 1 ); + if ($module eq "parameters") { + $template->param( CAN_user_management => 1 ); + } + } + } } } + if ( $in->{'type'} eq "intranet" ) { $template->param( intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"), @@ -375,6 +357,25 @@ that the user must have the "circulate" privilege in order to proceed. To make sure that access control is correct, the C<$flagsrequired> parameter must be specified correctly. +If the CheckSpecificUserPermissions system preference is ON, the +value of each key in the C hash takes on an additional +meaning, e.g., + +=item 1 + +The user must have access to all subfunctions of the module +specified by the hash key. + +=item * + +The user must have access to at least one subfunction of the module +specified by the hash key. + +=item specific permission, e.g., 'export_catalog' + +The user must have access to the specific subfunction list, which +must correspond to a row in the permissions table. + The C<$type> argument specifies whether the template should be retrieved from the opac or intranet directory tree. "opac" is assumed if it is not specified; however, if C<$type> is specified, @@ -1197,6 +1198,7 @@ C<$authflags> is a hashref of permissions sub getuserflags { my $flags = shift; + my $userid = shift; my $dbh = shift; my $userflags; $flags = 0 unless $flags; @@ -1211,15 +1213,101 @@ sub getuserflags { $userflags->{$flag} = 0; } } + + # get subpermissions and merge with top-level permissions + my $user_subperms = get_user_subpermissions($userid); + foreach my $module (keys %$user_subperms) { + next if $userflags->{$module} == 1; # user already has permission for everything in this module + $userflags->{$module} = $user_subperms->{$module}; + } + return $userflags; } +=item get_user_subpermissions + +=over 4 + +my $user_perm_hashref = get_user_subpermissions($userid); + +=back + +Given the userid (note, not the borrowernumber) of a staff user, +return a hashref of hashrefs of the specific subpermissions +accorded to the user. An example return is + +{ + tools => { + export_catalog => 1, + import_patrons => 1, + } +} + +The top-level hash-key is a module or function code from +userflags.flag, while the second-level key is a code +from permissions. + +The results of this function do not give a complete picture +of the functions that a staff user can access; it is also +necessary to check borrowers.flags. + +=cut + +sub get_user_subpermissions { + my $userid = shift; + + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT flag, code + FROM user_permissions + JOIN permissions USING (module_bit, code) + JOIN userflags ON (module_bit = bit) + JOIN borrowers USING (borrowernumber) + WHERE userid = ?"); + $sth->execute($userid); + + my $user_perms = {}; + while (my $perm = $sth->fetchrow_hashref) { + $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1; + } + return $user_perms; +} + +=item get_all_subpermissions + +=over 4 + +my $perm_hashref = get_all_subpermissions(); + +=back + +Returns a hashref of hashrefs defining all specific +permissions currently defined. The return value +has the same structure as that of C, +except that the innermost hash value is the description +of the subpermission. + +=cut + +sub get_all_subpermissions { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT flag, code, description + FROM permissions + JOIN userflags ON (module_bit = bit)"); + $sth->execute(); + + my $all_perms = {}; + while (my $perm = $sth->fetchrow_hashref) { + $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'}; + } + return $all_perms; +} + =item haspermission $flags = ($dbh,$member,$flagsrequired); C<$member> may be either userid or overloaded with $borrower hashref from GetMemberDetails. -C<$flags> is a hashref of required flags lik C<$borrower-<{authflags}> +C<$flags> is a hashref of required flags like C<$borrower-<{authflags}> Returns member's flags or 0 if a permission is not met. @@ -1235,7 +1323,7 @@ sub haspermission { my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE userid=?"); $sth->execute($userid); my ($intflags) = $sth->fetchrow; - $flags = getuserflags( $intflags, $dbh ); + $flags = getuserflags( $intflags, $userid, $dbh ); } if ( $userid eq C4::Context->config('user') ) { # Super User Account from /etc/koha.conf @@ -1246,8 +1334,18 @@ sub haspermission { $flags->{'superlibrarian'} = 1; } return $flags if $flags->{superlibrarian}; - foreach ( keys %$flagsrequired ) { - return 0 unless( $flags->{$_} ); + foreach my $module ( keys %$flagsrequired ) { + if (C4::Context->preference('CheckSpecificUserPermissions')) { + my $subperm = $flagsrequired->{$module}; + if ($subperm eq '*') { + return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) ); + } else { + return 0 unless ( $flags->{$module} == 1 or + ( exists $flags->{$module}->{$subperm} and $flags->{$module}->{$subperm} == 1 ) ); + } + } else { + return 0 unless ( $flags->{$module} ); + } } return $flags; #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered. -- 2.39.2