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

(-)a/Koha/REST/V1.pm (-19 / +165 lines)
Lines 20-25 use Mojo::Base 'Mojolicious'; Link Here
20
20
21
use C4::Auth qw( check_cookie_auth get_session haspermission );
21
use C4::Auth qw( check_cookie_auth get_session haspermission );
22
use C4::Context;
22
use C4::Context;
23
use Koha::Account::Lines;
24
use Koha::Issues;
25
use Koha::Holds;
26
use Koha::OldIssues;
23
use Koha::Patrons;
27
use Koha::Patrons;
24
28
25
sub startup {
29
sub startup {
Lines 68-97 sub authenticate_api_request { Link Here
68
            { error => "Authentication failure." },
72
            { error => "Authentication failure." },
69
            {},
73
            {},
70
            401
74
            401
71
        ) if $cookie and $action_spec->{'x-koha-permission'};
75
        ) if $cookie and $action_spec->{'x-koha-authorization'};
72
    }
76
    }
73
77
74
    if ($action_spec->{'x-koha-permission'}) {
78
    return $next->($c) unless $action_spec->{'x-koha-authorization'};
75
        return $c->render_swagger(
79
    unless ($user) {
76
            { error => "Authentication required." },
80
        return $c->render_swagger({ error => "Authentication required." },{},401);
77
            {},
81
    }
78
            401
79
        ) unless $user;
80
82
81
        if (C4::Auth::haspermission($user->userid, $action_spec->{'x-koha-permission'})) {
83
    my $authorization = $action_spec->{'x-koha-authorization'};
82
            return $next->($c);
84
    return $next->($c) if allow_owner($c, $authorization, $user);
83
        }
85
    return $next->($c) if allow_guarantor($c, $authorization, $user);
84
        else {
86
85
            return $c->render_swagger(
87
    my $permissions = $authorization->{'permissions'};
86
                { error => "Authorization failure. Missing required permission(s)." },
88
    return $next->($c) if C4::Auth::haspermission($user->userid, $permissions);
87
                {},
89
    return $c->render_swagger(
88
                403
90
        { error => "Authorization failure. Missing required permission(s)." },
89
            );
91
        {},
92
        403
93
    );
94
}
95
96
=head3 allow_owner
97
98
Allows access to object for its owner.
99
100
There are endpoints that should allow access for the object owner even if they
101
do not have the required permission, e.g. access an own reserve. This can be
102
achieved by defining the operation as follows:
103
104
"/holds/{reserve_id}": {
105
    "get": {
106
        ...,
107
        "x-koha-authorization": {
108
            "allow-owner": true,
109
            "permissions": {
110
                "borrowers": "1"
111
            }
90
        }
112
        }
91
    }
113
    }
92
    else {
114
}
93
        return $next->($c);
115
116
=cut
117
118
sub allow_owner {
119
    my ($c, $authorization, $user) = @_;
120
121
    return unless $authorization->{'allow-owner'};
122
123
    return check_object_ownership($c, $user) if $user and $c;
124
}
125
126
=head3 allow_guarantor
127
128
Same as "allow_owner", but checks if the object is owned by one of C<$user>'s
129
guarantees.
130
131
=cut
132
133
sub allow_guarantor {
134
    my ($c, $authorization, $user) = @_;
135
136
    if (!$c || !$user || !$authorization || !$authorization->{'allow-guarantor'}){
137
        return;
138
    }
139
140
    my $guarantees = $user->guarantees->as_list;
141
    foreach my $guarantee (@{$guarantees}) {
142
        return 1 if check_object_ownership($c, $guarantee);
94
    }
143
    }
95
}
144
}
96
145
146
=head3 check_object_ownership
147
148
Determines ownership of an object from request parameters.
149
150
As introducing an endpoint that allows access for object's owner; if the
151
parameter that will be used to determine ownership is not already inside
152
$parameters, add a new subroutine that checks the ownership and extend
153
$parameters to contain a key with parameter_name and a value of a subref to
154
the subroutine that you created.
155
156
=cut
157
158
sub check_object_ownership {
159
    my ($c, $user) = @_;
160
161
    return if not $c or not $user;
162
163
    my $parameters = {
164
        accountlines_id => \&_object_ownership_by_accountlines_id,
165
        borrowernumber  => \&_object_ownership_by_borrowernumber,
166
        checkout_id     => \&_object_ownership_by_checkout_id,
167
        reserve_id      => \&_object_ownership_by_reserve_id,
168
    };
169
170
    foreach my $param (keys $parameters) {
171
        my $check_ownership = $parameters->{$param};
172
        if ($c->stash($param)) {
173
            return &$check_ownership($c, $user, $c->stash($param));
174
        }
175
        elsif ($c->param($param)) {
176
            return &$check_ownership($c, $user, $c->param($param));
177
        }
178
        elsif ($c->req->json && $c->req->json->{$param}) {
179
            return 1 if &$check_ownership($c, $user, $c->req->json->{$param});
180
        }
181
    }
182
}
183
184
=head3 _object_ownership_by_accountlines_id
185
186
Finds a Koha::Account::Line-object by C<$accountlines_id> and checks if it
187
belongs to C<$user>.
188
189
=cut
190
191
sub _object_ownership_by_accountlines_id {
192
    my ($c, $user, $accountlines_id) = @_;
193
194
    my $accountline = Koha::Account::Lines->find($accountlines_id);
195
    return $accountline && $user->borrowernumber == $accountline->borrowernumber;
196
}
197
198
=head3 _object_ownership_by_borrowernumber
199
200
Compares C<$borrowernumber> to currently logged in C<$user>.
201
202
=cut
203
204
sub _object_ownership_by_borrowernumber {
205
    my ($c, $user, $borrowernumber) = @_;
206
207
    return $user->borrowernumber == $borrowernumber;
208
}
209
210
=head3 _object_ownership_by_checkout_id
211
212
First, attempts to find a Koha::Issue-object by C<$issue_id>. If we find one,
213
compare its borrowernumber to currently logged in C<$user>. However, if an issue
214
is not found, attempt to find a Koha::OldIssue-object instead and compare its
215
borrowernumber to currently logged in C<$user>.
216
217
=cut
218
219
sub _object_ownership_by_checkout_id {
220
    my ($c, $user, $issue_id) = @_;
221
222
    my $issue = Koha::Issues->find($issue_id);
223
    $issue = Koha::OldIssues->find($issue_id) unless $issue;
224
    return $issue && $issue->borrowernumber
225
            && $user->borrowernumber == $issue->borrowernumber;
226
}
227
228
=head3 _object_ownership_by_reserve_id
229
230
Finds a Koha::Hold-object by C<$reserve_id> and checks if it
231
belongs to C<$user>.
232
233
TODO: Also compare against old_reserves
234
235
=cut
236
237
sub _object_ownership_by_reserve_id {
238
    my ($c, $user, $reserve_id) = @_;
239
240
    my $reserve = Koha::Holds->find($reserve_id);
241
    return $reserve && $user->borrowernumber == $reserve->borrowernumber;
242
}
243
97
1;
244
1;
98
- 

Return to bug 14868