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

(-)a/Koha/Allowlist.pm (-40 / +53 lines)
Lines 1-7 Link Here
1
package Koha::Allowlist;
1
package Koha::Allowlist;
2
2
3
use Modern::Perl;
3
use Modern::Perl;
4
use Array::Utils qw( array_minus );
5
4
6
=head1 NAME
5
=head1 NAME
7
6
Lines 11-23 Koha::Allowlist - Allowlist implementation base class Link Here
11
10
12
=head2 Class Methods
11
=head2 Class Methods
13
12
14
    my $allow_list Koha::Allowlist->new({ interface => 'staff' });
15
16
=head3 new
13
=head3 new
17
14
18
Constructor.
15
    my $allow_list = Koha::Allowlist->new({ interface => 'staff' });
16
17
    Constructor.
19
18
20
Interface must be 'staff' or 'opac' - default to 'opac'
19
    Interface must be 'staff' or 'opac' - defaults to 'opac'
21
20
22
=cut
21
=cut
23
22
Lines 29-93 sub new { Link Here
29
      ( $args->{interface} && $args->{interface} eq 'staff' )
28
      ( $args->{interface} && $args->{interface} eq 'staff' )
30
      ? 'staff'
29
      ? 'staff'
31
      : 'opac';
30
      : 'opac';
31
    $self->{_entries} = {};
32
    $self->_get_entries if $self->can('_get_entries');
32
    return $self;
33
    return $self;
33
}
34
}
34
35
35
=head3 apply
36
=head3 add
36
37
37
    my $ui_fields = Koha::Allowlist->new({ interface => 'staff' })->apply({ input => $hashref, additional_deny_list => [qw( list of fields )] });
38
    $list->add( 'column1', { column2 => { staff => 1 }} );
38
39
40
=cut
39
41
40
Apply an allowlist to input data.
42
sub add {
43
    my ( $self, @args ) = @_;
44
    foreach my $arg ( @args ) {
45
        if( !ref($arg) ) {
46
            $self->{_entries}->{$arg} = 1;
47
        } elsif( ref($arg) eq 'HASH' ) {
48
            $self->{_entries} = { %{$self->{_entries}}, %$arg };
49
        }
50
    }
51
}
52
53
=head3 remove
54
55
    $list->remove( 'column3', 'column4' );
41
56
42
=cut
57
=cut
43
58
44
sub apply {
59
sub remove {
45
    my ( $self, $params ) = @_;
60
    my ( $self, @args ) = @_;
61
    foreach my $arg ( @args ) {
62
        delete $self->{_entries}->{$arg};
63
    }
64
}
46
65
47
    my $input = $params->{input};
66
=head3 apply
48
    my $additional_deny_list = $params->{additional_deny_list} || [];
49
67
50
    my $blocked = {};
68
    my $blocked = $allowlist->apply({ input => $hashref, verbose => 1 });
51
    my $ui_fields = { map { $_ => 1 } $self->get_ui_fields() };
52
    return unless $ui_fields and %$ui_fields;
53
69
54
    delete $ui_fields->{$_} for @$additional_deny_list;
70
    Apply an allowlist to input data. Note that we modify the $hashref argument!
71
    The verbose flag controls the warn statement.
55
72
56
    return unless $input  and %$input;
73
    Returns a hash of blocked entries. Might be useful for logging purposes?
57
74
58
    my @keys = keys %$input;
75
=cut
59
    foreach my $key (@keys){
60
        unless ( exists $ui_fields->{ $key } ) {
61
            #NOTE: We capture the deleted data so that it can be used for logging purposes
62
            $blocked->{$key} = delete $input->{ $key };
63
        }
64
    }
65
76
66
    if ( %$blocked ) {
77
sub apply {
67
        while ( my ($k, $v)=each %$blocked){
78
    my ( $self, $params ) = @_;
68
            # FIXME We should raise an exception from here
79
    my $input = $params->{input} or return;
69
            my @c = caller;
80
70
            warn sprintf "Forbidden - Tried to modify '%s' with '%s' from %s", $k, $v, "@c";
81
    my $blocked = {};
82
    foreach my $key ( keys %$input ){
83
        unless ( exists $self->{_entries}->{$key} ) {
84
            $blocked->{$key} = delete $input->{ $key };
71
        }
85
        }
72
    }
86
    }
73
87
    warn 'Koha::Allowlist: apply blocked one or more keys' if keys %$blocked && $params->{verbose};
88
    # TODO Pass details and caller to Koha::Logger ?
74
    return $blocked;
89
    return $blocked;
75
}
90
}
76
91
77
=head3 get_ui_fields
92
=head3 _get_entries
93
94
    my $fields = $self->_get_entries;
78
95
79
    my $ui_fields = $self->get_ui_fields();
96
    This routine should be overridden when subclassing. 
80
    my $ui_fields = Koha::Patron::Allowlist::Public->new->get_ui_fields
81
97
82
=cut
98
=cut
83
99
84
sub get_ui_fields {
100
sub _get_entries {
85
    my ($self)           = @_;
101
    my ( $self ) = @_;
86
    my @all_fields       = $self->_get_all_fields();
102
    # You could do something here like:
87
    my @global_deny_list = $self->_global_deny_list();
103
    # $self->add( 'myfield' );
88
    my @deny_list        = ( $self->{interface} eq 'staff' ) ? $self->_deny_list_staff() : $self->_deny_list_opac();
89
    my @global_allow_list = array_minus @all_fields, @global_deny_list;
90
    return array_minus @global_allow_list, @deny_list;
91
}
104
}
92
105
93
1;
106
1;
(-)a/Koha/Patron/Allowlist.pm (-58 / +7 lines)
Lines 1-12 Link Here
1
package Koha::Patron::Allowlist;
1
package Koha::Patron::Allowlist;
2
2
3
use Modern::Perl;
3
use parent 'Koha::Allowlist';
4
use parent 'Koha::Allowlist';
4
5
5
# This must not be replaced by Koha::Patrons->columns
6
sub _get_entries {
6
# We want developper to not forget to adjust it manually and add new attribute to the deny list if needed
7
    my ( $self ) = @_;
7
sub _get_all_fields {
8
    my @allowed = qw(
8
    return qw(
9
      borrowernumber
10
      cardnumber
9
      cardnumber
11
      surname
10
      surname
12
      firstname
11
      firstname
Lines 40-65 sub _get_all_fields { Link Here
40
      dateofbirth
39
      dateofbirth
41
      branchcode
40
      branchcode
42
      categorycode
41
      categorycode
43
      dateenrolled
44
      dateexpiry
45
      date_renewed
46
      gonenoaddress
47
      lost
48
      debarred
49
      debarredcomment
50
      contactname
42
      contactname
51
      contactfirstname
43
      contactfirstname
52
      contacttitle
44
      contacttitle
53
      borrowernotes
54
      relationship
55
      sex
45
      sex
56
      password
46
      password
57
      flags
58
      userid
47
      userid
59
      opacnote
60
      contactnote
48
      contactnote
61
      sort1
62
      sort2
63
      altcontactfirstname
49
      altcontactfirstname
64
      altcontactsurname
50
      altcontactsurname
65
      altcontactaddress1
51
      altcontactaddress1
Lines 70-118 sub _get_all_fields { Link Here
70
      altcontactcountry
56
      altcontactcountry
71
      altcontactphone
57
      altcontactphone
72
      smsalertnumber
58
      smsalertnumber
73
      sms_provider_id
74
      privacy
75
      privacy_guarantor_fines
76
      privacy_guarantor_checkouts
77
      checkprevcheckout
78
      updated_on
79
      lastseen
80
      lang
81
      login_attempts
82
      overdrive_auth_token
83
      anonymized
84
      autorenew_checkouts
85
      primary_contact_method
59
      primary_contact_method
86
    );
60
    );
87
}
61
    $self->add( @allowed );
88
89
sub _global_deny_list {
90
    return qw(
91
      borrowernumber
92
      date_renewed
93
      debarred
94
      debarredcomment
95
      flags
96
      privacy
97
      privacy_guarantor_fines
98
      privacy_guarantor_checkouts
99
      checkprevcheckout
100
      updated_on
101
      lastseen
102
      lang
103
      login_attempts
104
      overdrive_auth_token
105
      anonymized
106
    );
107
}
108
62
109
sub _deny_list_opac {
63
    my @allowed_staff = qw(
110
    return qw(
111
      dateenrolled
64
      dateenrolled
112
      dateexpiry
65
      dateexpiry
113
      gonenoaddress
66
      gonenoaddress
114
      lost
67
      lost
115
      borrowernotes
116
      relationship
68
      relationship
117
      opacnote
69
      opacnote
118
      sort1
70
      sort1
Lines 120-129 sub _deny_list_opac { Link Here
120
      sms_provider_id
72
      sms_provider_id
121
      autorenew_checkouts
73
      autorenew_checkouts
122
    );
74
    );
123
}
75
    $self->add( @allowed_staff ) if $self->{interface} eq 'staff';
124
125
sub _deny_list_staff {
126
    return qw();
127
}
76
}
128
77
129
1;
78
1;
(-)a/t/db_dependent/Koha/Patron/Allowlist.t (-76 / +22 lines)
Lines 16-101 Link Here
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
use Test::More tests => 1;
20
use Test::More tests => 4;
21
22
use Test::MockModule;
23
use Test::Exception;
24
use Test::Warn;
25
26
use Koha::Patrons;
27
use Koha::Patron::Allowlist;
20
use Koha::Patron::Allowlist;
28
21
29
use t::lib::Mocks;
22
subtest 'Patron::Allowlist' => sub {
23
    plan tests => 12;
30
24
31
subtest '_get_all_fields' => sub {
25
    my $input = { firstname => 'test firstname', surname => 'test surname', flags => '1', gonenoaddress => 1, unexist => 2 };
32
    my @columns = Koha::Patrons->columns;
33
    is_deeply(
34
        \@columns,
35
        [ Koha::Patron::Allowlist->_get_all_fields ],
36
        'All DB columns must be listed in _get_all_fields'
37
    );
38
};
39
40
subtest 'Staff Allowlist' => sub {
41
    plan tests => 4;
42
    my $input = { firstname => 'test firstname', surname => 'test surname', flags => '1' };
43
    my $allowlist = Koha::Patron::Allowlist->new({ interface => 'staff' });
26
    my $allowlist = Koha::Patron::Allowlist->new({ interface => 'staff' });
44
    warning_like {
27
    my $blocked = $allowlist->apply({ input => $input });
45
        $allowlist->apply({ input => $input, });
46
    } qr{Forbidden - Tried to modify 'flags' with '1' from};
47
    is( $input->{firstname}, 'test firstname', 'firstname preserved' );
48
    is( $input->{surname}, 'test surname', 'surname preserved' );
49
    is( $input->{flags}, undef, 'flags filtered' );
50
};
51
52
subtest 'Public Allowlist' => sub {
53
    plan tests => 4;
54
    my $input = { firstname => 'test firstname', surname => 'test surname', flags => '1' };
55
    my $allowlist = Koha::Patron::Allowlist->new();
56
    warning_like {
57
        $allowlist->apply({ input => $input, });
58
    } qr{Forbidden - Tried to modify 'flags' with '1' from};
59
28
60
    is( $input->{firstname}, 'test firstname', 'firstname preserved' );
29
    is( $blocked->{unexist}, 2, "Unexist got blocked" );
61
    is( $input->{surname},  'test surname', 'surname preserved' );
30
    is( $input->{unexist}, undef, 'unexist filtered' );
31
    is( $blocked->{flags}, 1, "Flags got blocked" );
62
    is( $input->{flags}, undef, 'flags filtered' );
32
    is( $input->{flags}, undef, 'flags filtered' );
63
};
64
65
subtest 'additional_deny_list' => sub {
66
    plan tests => 10;
67
68
    my $input = { firstname => 'test firstname', surname => 'test surname' };
69
    my $allowlist = Koha::Patron::Allowlist->new();
70
71
    $allowlist->apply({
72
        input => $input,
73
        additional_deny_list => [],
74
    });
75
76
    is( $input->{firstname}, 'test firstname', 'firstname preserved' );
33
    is( $input->{firstname}, 'test firstname', 'firstname preserved' );
77
    is( $input->{surname},   'test surname',   'surname filtered' );
34
    is( $input->{surname}, 'test surname', 'surname preserved' );
78
    is( $input->{flags},     undef,            'flags filtered' );
35
    is( $input->{gonenoaddress}, 1, 'gonenoaddress preserved on staff' );
79
36
80
    $allowlist->apply({
37
    $input = { firstname => 'test firstname', surname => 'test surname', flags => '1', gonenoaddress => 1, unexist => 2 };
81
        input => $input,
38
    my $opacallowlist = Koha::Patron::Allowlist->new;
82
        additional_deny_list => ['not_here'],
39
    $blocked = $opacallowlist->apply({ input => $input });
83
    });
40
    is( $blocked->{unexist}, 2, "Unexist got blocked on opac too");
84
41
    is( $blocked->{flags}, 1, "Flags got blocked on opac too" );
85
    is( $input->{firstname}, 'test firstname', 'firstname preserved' );
42
    is( $blocked->{gonenoaddress}, 1, "Gonenoaddress got blocked on opac now" );
86
    is( $input->{surname},   'test surname',   'surname filtered' );
43
    is( $input->{gonenoaddress}, undef, 'gonenoaddress filtered, opac' );
87
    is( $input->{flags},     undef,            'flags filtered' );
44
    is( $input->{surname}, 'test surname', 'surname preserved, opac' );
88
89
    warning_like {
90
        $allowlist->apply({
91
            input => $input,
92
            additional_deny_list => ['surname'],
93
        });
94
    }
95
    qr{Forbidden - Tried to modify 'surname' with 'test surname' from};
96
97
    is( $input->{firstname}, 'test firstname', 'firstname preserved' );
98
    is( $input->{surname},   undef,            'surname filtered' );
99
    is( $input->{flags},     undef,            'flags filtered' );
100
101
};
45
};
102
- 
46
47
#subtest 'Additional adds or removes' => sub {
48
#

Return to bug 28935