View | Details | Raw Unified | Return to bug 22031
Collapse All | Expand All

(-)a/C4/Auth.pm (-18 / +41 lines)
Lines 19-24 package C4::Auth; Link Here
19
19
20
use strict;
20
use strict;
21
use warnings;
21
use warnings;
22
use Carp qw/croak/;
23
22
use Digest::MD5 qw(md5_base64);
24
use Digest::MD5 qw(md5_base64);
23
use JSON qw/encode_json/;
25
use JSON qw/encode_json/;
24
use URI::Escape;
26
use URI::Escape;
Lines 2025-2036 sub get_all_subpermissions { Link Here
2025
  $flags = ($userid, $flagsrequired);
2027
  $flags = ($userid, $flagsrequired);
2026
2028
2027
C<$userid> the userid of the member
2029
C<$userid> the userid of the member
2028
C<$flags> is a hashref of required flags like C<$borrower-&lt;{authflags}> 
2030
C<$flags> is a query structure similar to that used by SQL::Abstract that
2031
denotes the combination of flags required.
2032
2033
The main logic of this method is that things in arrays are OR'ed, and things
2034
in hashes are AND'ed.
2029
2035
2030
Returns member's flags or 0 if a permission is not met.
2036
Returns member's flags or 0 if a permission is not met.
2031
2037
2032
=cut
2038
=cut
2033
2039
2040
sub _dispatch {
2041
    my ($required, $flags) = @_;
2042
2043
    my $ref = ref($required);
2044
    if ($ref eq '') {
2045
        if ($required eq '*') {
2046
            return 0 unless ( $flags or ref( $flags ) );
2047
        } else {
2048
            return 0 unless ( $flags and (!ref( $flags ) || $flags->{$required} ));
2049
        }
2050
    } elsif ($ref eq 'HASH') {
2051
        foreach my $key (keys %{$required}) {
2052
            my $require = $required->{$key};
2053
            my $rflags  = $flags->{$key};
2054
            return 0 unless _dispatch($require, $rflags);
2055
        }
2056
    } elsif ($ref eq 'ARRAY') {
2057
        my $satisfied = 0;
2058
        foreach my $require ( @{$required} ) {
2059
            my $rflags =
2060
              ( ref($flags) && !ref($require) && ( $require ne '*' ) )
2061
              ? $flags->{$require}
2062
              : $flags;
2063
            $satisfied++ if _dispatch( $require, $rflags );
2064
        }
2065
        return 0 unless $satisfied;
2066
    } else {
2067
        croak "Unexpected structure found: $ref";
2068
    }
2069
2070
    return $flags;
2071
};
2072
2034
sub haspermission {
2073
sub haspermission {
2035
    my ( $userid, $flagsrequired ) = @_;
2074
    my ( $userid, $flagsrequired ) = @_;
2036
    my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
2075
    my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
Lines 2039-2061 sub haspermission { Link Here
2039
    my $flags = getuserflags( $row, $userid );
2078
    my $flags = getuserflags( $row, $userid );
2040
2079
2041
    return $flags if $flags->{superlibrarian};
2080
    return $flags if $flags->{superlibrarian};
2042
2081
    return _dispatch($flagsrequired, $flags);
2043
    foreach my $module ( keys %$flagsrequired ) {
2044
        my $subperm = $flagsrequired->{$module};
2045
        if ( $subperm eq '*' ) {
2046
            return 0 unless ( $flags->{$module} == 1 or ref( $flags->{$module} ) );
2047
        } else {
2048
            return 0 unless (
2049
                ( defined $flags->{$module} and
2050
                    $flags->{$module} == 1 )
2051
                or
2052
                ( ref( $flags->{$module} ) and
2053
                    exists $flags->{$module}->{$subperm} and
2054
                    $flags->{$module}->{$subperm} == 1 )
2055
            );
2056
        }
2057
    }
2058
    return $flags;
2059
2082
2060
    #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
2083
    #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
2061
}
2084
}
(-)a/t/db_dependent/Auth/haspermission.t (-76 / +173 lines)
Lines 20-26 Link Here
20
# along with Koha; if not, see <http://www.gnu.org/licenses>.
20
# along with Koha; if not, see <http://www.gnu.org/licenses>.
21
21
22
use Modern::Perl;
22
use Modern::Perl;
23
use Test::More tests => 13;
23
use Test::More tests => 3;
24
24
25
use Koha::Database;
25
use Koha::Database;
26
use t::lib::TestBuilder;
26
use t::lib::TestBuilder;
Lines 31-108 $schema->storage->txn_begin; Link Here
31
31
32
# Adding two borrowers and granular permissions for the second borrower
32
# Adding two borrowers and granular permissions for the second borrower
33
my $builder = t::lib::TestBuilder->new();
33
my $builder = t::lib::TestBuilder->new();
34
my $borr1 = $builder->build({
34
my $borr1   = $builder->build(
35
    source => 'Borrower',
35
    {
36
    value  => {
36
        source => 'Borrower',
37
        surname => 'Superlib',
37
        value  => {
38
        flags   => 1,
38
            surname => 'Superlib',
39
    },
39
            flags   => 1,
40
});
40
        },
41
my $borr2 = $builder->build({
41
    }
42
    source => 'Borrower',
42
);
43
    value  => {
43
my $borr2 = $builder->build(
44
        surname => 'Bor2',
44
    {
45
        flags   => 2 + 4 + 2**11, # circulate, catalogue, acquisition
45
        source => 'Borrower',
46
    },
46
        value  => {
47
});
47
            surname => 'Bor2',
48
$builder->build({
48
            flags   => 2 + 4 + 2**11,    # circulate, catalogue, acquisition
49
    source => 'UserPermission',
49
        },
50
    value  => {
50
    }
51
        borrowernumber => $borr2->{borrowernumber},
51
);
52
        module_bit => 13, # tools
52
$builder->build(
53
        code => 'upload_local_cover_images',
53
    {
54
    },
54
        source => 'UserPermission',
55
});
55
        value  => {
56
$builder->build({
56
            borrowernumber => $borr2->{borrowernumber},
57
    source => 'UserPermission',
57
            module_bit     => 13,                            # tools
58
    value  => {
58
            code           => 'upload_local_cover_images',
59
        borrowernumber => $borr2->{borrowernumber},
59
        },
60
        module_bit => 13, # tools
60
    }
61
        code => 'batch_upload_patron_images',
61
);
62
    },
62
$builder->build(
63
});
63
    {
64
64
        source => 'UserPermission',
65
# Check top level permission for superlibrarian
65
        value  => {
66
my $r = haspermission( $borr1->{userid},
66
            borrowernumber => $borr2->{borrowernumber},
67
    { circulate => 1, editcatalogue => 1 } );
67
            module_bit     => 13,                             # tools
68
is( ref($r), 'HASH', 'Superlibrarian/circulate' );
68
            code           => 'batch_upload_patron_images',
69
69
        },
70
# Check specific top level permission(s) for borr2
70
    }
71
$r = haspermission( $borr2->{userid},
71
);
72
    { circulate => 1, catalogue => 1 } );
72
73
is( ref($r), 'HASH', 'Borrower2/circulate' );
73
subtest 'scalar top level tests' => sub {
74
$r = haspermission( $borr2->{userid}, { updatecharges => 1 } );
74
75
is( $r, 0, 'Borrower2/updatecharges should fail' );
75
    plan tests => 3;
76
76
77
# Check granular permission with 1: means all subpermissions
77
    # Check top level permission for superlibrarian
78
$r = haspermission( $borr1->{userid}, { tools => 1 } );
78
    my $r = haspermission( $borr1->{userid}, 'circulate' );
79
is( ref($r), 'HASH', 'Superlibrarian/tools granular all' );
79
    is( ref($r), 'HASH', 'Superlibrarian/circulate' );
80
$r = haspermission( $borr2->{userid}, { tools => 1 } );
80
81
is( $r, 0, 'Borrower2/tools granular all should fail' );
81
    # Check specific top level permission(s) for borr2
82
82
    $r = haspermission( $borr2->{userid}, 'circulate' );
83
# Check granular permission with *: means at least one subpermission
83
    is( ref($r), 'HASH', 'Borrower2/circulate' );
84
$r = haspermission( $borr1->{userid}, { tools => '*' } );
84
    $r = haspermission( $borr2->{userid}, 'updatecharges' );
85
is( ref($r), 'HASH', 'Superlibrarian/tools granular *' );
85
    is( $r, 0, 'Borrower2/updatecharges should fail' );
86
$r = haspermission( $borr2->{userid}, { acquisition => '*' } );
86
};
87
is( ref($r), 'HASH', 'Borrower2/acq granular *' );
87
88
$r = haspermission( $borr2->{userid}, { tools => '*' } );
88
subtest 'hashref top level AND tests' => sub {
89
is( ref($r), 'HASH', 'Borrower2/tools granular *' );
89
90
$r = haspermission( $borr2->{userid}, { serials => '*' } );
90
    plan tests => 15;
91
is( $r, 0, 'Borrower2/serials granular * should fail' );
91
92
92
    # Check top level permission for superlibrarian
93
# Check granular permission with one or more specific subperms
93
    my $r =
94
$r = haspermission( $borr1->{userid}, { tools => 'edit_news' } );
94
      haspermission( $borr1->{userid}, { circulate => 1 } );
95
is( ref($r), 'HASH', 'Superlibrarian/tools edit_news' );
95
    is( ref($r), 'HASH', 'Superlibrarian/circulate' );
96
$r = haspermission( $borr2->{userid}, { acquisition => 'budget_manage' } );
96
97
is( ref($r), 'HASH', 'Borrower2/acq budget_manage' );
97
    # Check specific top level permission(s) for borr2
98
$r = haspermission( $borr2->{userid},
98
    $r = haspermission( $borr2->{userid}, { circulate => 1, catalogue => 1 } );
99
    { acquisition => 'budget_manage', tools => 'edit_news' } );
99
    is( ref($r), 'HASH', 'Borrower2/circulate' );
100
is( $r, 0, 'Borrower2/two granular should fail' );
100
    $r = haspermission( $borr2->{userid}, { updatecharges => 1 } );
101
$r = haspermission( $borr2->{userid}, {
101
    is( $r, 0, 'Borrower2/updatecharges should fail' );
102
    tools => 'upload_local_cover_images',
102
103
    tools => 'batch_upload_patron_images',
103
    # Check granular permission with 1: means all subpermissions
104
});
104
    $r = haspermission( $borr1->{userid}, { tools => 1 } );
105
is( ref($r), 'HASH', 'Borrower2/tools granular two upload subperms' );
105
    is( ref($r), 'HASH', 'Superlibrarian/tools granular all' );
106
106
    $r = haspermission( $borr2->{userid}, { tools => 1 } );
107
# End
107
    is( $r, 0, 'Borrower2/tools granular all should fail' );
108
109
    # Check granular permission with *: means at least one subpermission
110
    $r = haspermission( $borr1->{userid}, { tools => '*' } );
111
    is( ref($r), 'HASH', 'Superlibrarian/tools granular *' );
112
    $r = haspermission( $borr2->{userid}, { acquisition => '*' } );
113
    is( ref($r), 'HASH', 'Borrower2/acq granular *' );
114
    $r = haspermission( $borr2->{userid}, { tools => '*' } );
115
    is( ref($r), 'HASH', 'Borrower2/tools granular *' );
116
    $r = haspermission( $borr2->{userid}, { serials => '*' } );
117
    is( $r, 0, 'Borrower2/serials granular * should fail' );
118
119
    # Check granular permission with one or more specific subperms
120
    $r = haspermission( $borr1->{userid}, { tools => 'edit_news' } );
121
    is( ref($r), 'HASH', 'Superlibrarian/tools edit_news' );
122
    $r = haspermission( $borr2->{userid}, { acquisition => 'budget_manage' } );
123
    is( ref($r), 'HASH', 'Borrower2/acq budget_manage' );
124
    $r = haspermission( $borr2->{userid},
125
        { acquisition => 'budget_manage', tools => 'edit_news' } );
126
    is( $r, 0, 'Borrower2 (/acquisition|budget_manage AND /tools|edit_news) should fail' );
127
    $r = haspermission(
128
        $borr2->{userid},
129
        {
130
            tools => {
131
                'upload_local_cover_images'  => 1,
132
                'batch_upload_patron_images' => 1
133
            },
134
        }
135
    );
136
    is( ref($r), 'HASH', 'Borrower2 (/tools|upload_local_cover_image AND /tools|batch_upload_patron_images) granular' );
137
    $r = haspermission(
138
        $borr2->{userid},
139
        {
140
            tools => {
141
                'upload_local_cover_images'  => 1,
142
                'edit_news' => 1
143
            },
144
        }
145
    );
146
    is( $r, 0, 'Borrower2 (/tools|upload_local_cover_image AND /tools|edit_news) granular' );
147
    $r = haspermission(
148
        $borr2->{userid},
149
        {
150
            tools => [ 'upload_local_cover_images', 'edit_news'],
151
        }
152
    );
153
    is( ref($r), 'HASH', 'Borrower2 (/tools|upload_local_cover_image OR /tools|edit_news) granular' );
154
};
155
156
subtest 'arrayref top level OR tests' => sub {
157
158
    plan tests => 13;
159
160
    # Check top level permission for superlibrarian
161
    my $r =
162
      haspermission( $borr1->{userid}, [ 'circulate', 'editcatalogue' ] );
163
    is( ref($r), 'HASH', 'Superlibrarian/circulate' );
164
165
    # Check specific top level permission(s) for borr2
166
    $r = haspermission( $borr2->{userid}, [ 'circulate', 'updatecharges' ] );
167
    is( ref($r), 'HASH', 'Borrower2/circulate OR Borrower2/updatecharges' );
168
    $r = haspermission( $borr2->{userid}, ['updatecharges', 'serials' ] );
169
    is( $r, 0, 'Borrower2/updatecharges OR Borrower2/serials should fail' );
170
171
    # Check granular permission with 1: means all subpermissions
172
    $r = haspermission( $borr1->{userid}, [ 'tools' ] );
173
    is( ref($r), 'HASH', 'Superlibrarian/tools granular all' );
174
    $r = haspermission( $borr2->{userid}, [ 'tools' ] );
175
    is( $r, 0, 'Borrower2/tools granular all should fail' );
176
177
    # Check granular permission with *: means at least one subpermission
178
    $r = haspermission( $borr1->{userid}, [ { tools => '*' } ] );
179
    is( ref($r), 'HASH', 'Superlibrarian/tools granular *' );
180
    $r = haspermission( $borr2->{userid}, [ { acquisition => '*' } ] );
181
    is( ref($r), 'HASH', 'Borrower2/acq granular *' );
182
    $r = haspermission( $borr2->{userid}, [ { tools => '*' } ] );
183
    is( ref($r), 'HASH', 'Borrower2/tools granular *' );
184
    $r = haspermission( $borr2->{userid}, [ { serials => '*' } ] );
185
    is( $r, 0, 'Borrower2/serials granular * should fail' );
186
187
    # Check granular permission with one or more specific subperms
188
    $r = haspermission( $borr1->{userid}, [ { tools => 'edit_news' } ] );
189
    is( ref($r), 'HASH', 'Superlibrarian/tools edit_news' );
190
    $r =
191
      haspermission( $borr2->{userid}, [ { acquisition => 'budget_manage' } ] );
192
    is( ref($r), 'HASH', 'Borrower2/acq budget_manage' );
193
    $r = haspermission( $borr2->{userid},
194
        [ { acquisition => 'budget_manage'}, { tools => 'edit_news' } ] );
195
    is( ref($r), 'HASH', 'Borrower2/two granular OR should pass' );
196
    $r = haspermission(
197
        $borr2->{userid},
198
        [
199
            { tools => ['upload_local_cover_images'] },
200
            { tools => ['edit_news'] }
201
        ]
202
    );
203
    is( ref($r), 'HASH', 'Borrower2/tools granular OR subperms' );
204
};
205
108
$schema->storage->txn_rollback;
206
$schema->storage->txn_rollback;
109
- 

Return to bug 22031