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

Return to bug 14868