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

(-)a/Koha/REST/V1/Patron.pm (-10 lines)
Lines 26-34 sub list { Link Here
26
    my ($c, $args, $cb) = @_;
26
    my ($c, $args, $cb) = @_;
27
27
28
    my $user = $c->stash('koha.user');
28
    my $user = $c->stash('koha.user');
29
    unless ($user && haspermission($user->userid, {borrowers => 1})) {
30
        return $c->$cb({error => "You don't have the required permission"}, 403);
31
    }
32
29
33
    my $patrons = Koha::Patrons->search;
30
    my $patrons = Koha::Patrons->search;
34
31
Lines 40-52 sub get { Link Here
40
37
41
    my $user = $c->stash('koha.user');
38
    my $user = $c->stash('koha.user');
42
39
43
    unless ( $user
44
        && ( $user->borrowernumber == $args->{borrowernumber}
45
            || haspermission($user->userid, {borrowers => 1}) ) )
46
    {
47
        return $c->$cb({error => "You don't have the required permission"}, 403);
48
    }
49
50
    my $patron = Koha::Patrons->find($args->{borrowernumber});
40
    my $patron = Koha::Patrons->find($args->{borrowernumber});
51
    unless ($patron) {
41
    unless ($patron) {
52
        return $c->$cb({error => "Patron not found"}, 404);
42
        return $c->$cb({error => "Patron not found"}, 404);
(-)a/api/v1/swagger/paths/holds.json (+22 lines)
Lines 21-26 Link Here
21
            "$ref": "../definitions.json#/error"
21
            "$ref": "../definitions.json#/error"
22
          }
22
          }
23
        }
23
        }
24
      },
25
      "x-koha-permission": {
26
        "allow-owner": "1",
27
        "permissions": {
28
          "borrowers": "1"
29
        }
24
      }
30
      }
25
    },
31
    },
26
    "post": {
32
    "post": {
Lines 92-97 Link Here
92
            "$ref": "../definitions.json#/error"
98
            "$ref": "../definitions.json#/error"
93
          }
99
          }
94
        }
100
        }
101
      },
102
      "x-koha-permission": {
103
        "allow-owner": "1",
104
        "permissions": {
105
          "reserveforothers": "1"
106
        }
95
      }
107
      }
96
    }
108
    }
97
  },
109
  },
Lines 148-153 Link Here
148
            "$ref": "../definitions.json#/error"
160
            "$ref": "../definitions.json#/error"
149
          }
161
          }
150
        }
162
        }
163
      },
164
      "x-koha-permission": {
165
        "permissions": {
166
          "reserveforothers": "1"
167
        }
151
      }
168
      }
152
    },
169
    },
153
    "delete": {
170
    "delete": {
Lines 171-176 Link Here
171
            "$ref": "../definitions.json#/error"
188
            "$ref": "../definitions.json#/error"
172
          }
189
          }
173
        }
190
        }
191
      },
192
      "x-koha-permission": {
193
        "permissions": {
194
          "reserveforothers": "1"
195
        }
174
      }
196
      }
175
    }
197
    }
176
  }
198
  }
(-)a/api/v1/swagger/paths/patrons.json (+11 lines)
Lines 22-27 Link Here
22
            "$ref": "../definitions.json#/error"
22
            "$ref": "../definitions.json#/error"
23
          }
23
          }
24
        }
24
        }
25
      },
26
      "x-koha-permission": {
27
        "permissions": {
28
            "borrowers": "1"
29
        }
25
      }
30
      }
26
    }
31
    }
27
  },
32
  },
Lines 55-60 Link Here
55
            "$ref": "../definitions.json#/error"
60
            "$ref": "../definitions.json#/error"
56
          }
61
          }
57
        }
62
        }
63
      },
64
      "x-koha-permission": {
65
        "allow-owner": "1",
66
        "permissions": {
67
            "borrowers": "1"
68
        }
58
      }
69
      }
59
    }
70
    }
60
  }
71
  }
(-)a/t/db_dependent/api/v1/holds.t (-22 / +155 lines)
Lines 17-24 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 30;
20
use Test::More tests => 60;
21
use Test::Mojo;
21
use Test::Mojo;
22
use t::lib::TestBuilder;
22
23
23
use DateTime;
24
use DateTime;
24
25
Lines 30-58 use C4::Reserves; Link Here
30
use Koha::Database;
31
use Koha::Database;
31
use Koha::Patron;
32
use Koha::Patron;
32
33
34
my $builder = t::lib::TestBuilder->new();
35
33
my $dbh = C4::Context->dbh;
36
my $dbh = C4::Context->dbh;
34
$dbh->{AutoCommit} = 0;
37
$dbh->{AutoCommit} = 0;
35
$dbh->{RaiseError} = 1;
38
$dbh->{RaiseError} = 1;
36
39
40
$ENV{REMOTE_ADDR} = '127.0.0.1';
37
my $t = Test::Mojo->new('Koha::REST::V1');
41
my $t = Test::Mojo->new('Koha::REST::V1');
38
42
39
my $categorycode = Koha::Database->new()->schema()->resultset('Category')->first()->categorycode();
43
my $categorycode = Koha::Database->new()->schema()->resultset('Category')->first()->categorycode();
40
my $branchcode = Koha::Database->new()->schema()->resultset('Branch')->first()->branchcode();
44
my $branchcode = Koha::Database->new()->schema()->resultset('Branch')->first()->branchcode();
41
45
46
# Create test users
47
48
# User without any permissions
49
my $nopermission = $builder->build({
50
    source => 'Borrower',
51
    value => {
52
        branchcode   => $branchcode,
53
        categorycode => $categorycode,
54
        flags        => 0
55
    }
56
});
57
my $session_nopermission = C4::Auth::get_session('');
58
$session_nopermission->param('number', $nopermission->{ borrowernumber });
59
$session_nopermission->param('id', $nopermission->{ userid });
60
$session_nopermission->param('ip', '127.0.0.1');
61
$session_nopermission->param('lasttime', time());
62
$session_nopermission->flush;
63
64
# User with borrowers and reserveforothers
42
my $borrower = Koha::Patron->new;
65
my $borrower = Koha::Patron->new;
43
$borrower->categorycode( $categorycode );
66
$borrower->categorycode( $categorycode );
44
$borrower->branchcode( $branchcode );
67
$borrower->branchcode( $branchcode );
45
$borrower->surname("Test Surname");
68
$borrower->surname("Test Surname");
69
$borrower->flags(80); # borrowers and reserveforothers flag
70
$borrower->userid($nopermission->{ userid }."z");
46
$borrower->store;
71
$borrower->store;
47
my $borrowernumber = $borrower->borrowernumber;
72
my $borrowernumber = $borrower->borrowernumber;
48
73
74
# User with borrowers
49
my $borrower2 = Koha::Patron->new;
75
my $borrower2 = Koha::Patron->new;
50
$borrower2->categorycode( $categorycode );
76
$borrower2->categorycode( $categorycode );
51
$borrower2->branchcode( $branchcode );
77
$borrower2->branchcode( $branchcode );
52
$borrower2->surname("Test Surname 2");
78
$borrower2->surname("Test Surname 2");
79
$borrower2->userid($nopermission->{ userid }."x");
80
$borrower2->flags(16); # borrowers flag
53
$borrower2->store;
81
$borrower2->store;
54
my $borrowernumber2 = $borrower2->borrowernumber;
82
my $borrowernumber2 = $borrower2->borrowernumber;
55
83
84
# User with reserveforothers
85
my $borrower3 = $builder->build({
86
    source => 'Borrower',
87
    value => {
88
        branchcode   => $branchcode,
89
        categorycode => $categorycode,
90
        flags        => 64 # reserveforothers flag
91
    }
92
});
93
94
# Get sessions
95
my $session = C4::Auth::get_session('');
96
$session->param('number', $borrower->borrowernumber);
97
$session->param('id', $borrower->userid);
98
$session->param('ip', '127.0.0.1');
99
$session->param('lasttime', time());
100
$session->flush;
101
my $session2 = C4::Auth::get_session('');
102
$session2->param('number', $borrower2->borrowernumber);
103
$session2->param('id', $borrower2->userid);
104
$session2->param('ip', '127.0.0.1');
105
$session2->param('lasttime', time());
106
$session2->flush;
107
my $session3 = C4::Auth::get_session('');
108
$session3->param('number', $borrower3->{ borrowernumber });
109
$session3->param('id', $borrower3->{ userid });
110
$session3->param('ip', '127.0.0.1');
111
$session3->param('lasttime', time());
112
$session3->flush;
113
56
my $biblionumber = create_biblio('RESTful Web APIs');
114
my $biblionumber = create_biblio('RESTful Web APIs');
57
my $itemnumber = create_item($biblionumber, 'TEST000001');
115
my $itemnumber = create_item($biblionumber, 'TEST000001');
58
116
Lines 60-97 my $reserve_id = C4::Reserves::AddReserve($branchcode, $borrowernumber, Link Here
60
    $biblionumber, undef, 1, undef, undef, undef, '', $itemnumber);
118
    $biblionumber, undef, 1, undef, undef, undef, '', $itemnumber);
61
119
62
# Add another reserve to be able to change first reserve's rank
120
# Add another reserve to be able to change first reserve's rank
63
C4::Reserves::AddReserve($branchcode, $borrowernumber2,
121
my $reserve_id2 = C4::Reserves::AddReserve($branchcode, $borrowernumber2,
64
    $biblionumber, undef, 2, undef, undef, undef, '', $itemnumber);
122
    $biblionumber, undef, 2, undef, undef, undef, '', $itemnumber);
65
123
66
my $suspend_until = DateTime->now->add(days => 10)->ymd;
124
my $suspend_until = DateTime->now->add(days => 10)->ymd;
125
my $expirationdate = DateTime->now->add(days => 10)->ymd;
126
127
my $post_data = {
128
    borrowernumber => int($borrowernumber),
129
    biblionumber => int($biblionumber),
130
    itemnumber => int($itemnumber),
131
    branchcode => $branchcode,
132
    expirationdate => $expirationdate,
133
};
67
my $put_data = {
134
my $put_data = {
68
    priority => 2,
135
    priority => 2,
69
    suspend_until => $suspend_until,
136
    suspend_until => $suspend_until,
70
};
137
};
71
$t->put_ok("/api/v1/holds/$reserve_id" => json => $put_data)
138
139
# Test endpoints without authentication, expecting 401 Unauthorized
140
$t->get_ok('/api/v1/holds')
141
  ->status_is(401);
142
$t->post_ok('/api/v1/holds')
143
  ->status_is(401);
144
$t->put_ok('/api/v1/holds/0')
145
  ->status_is(401);
146
$t->delete_ok('/api/v1/holds/0')
147
  ->status_is(401);
148
149
# Test endpoints with authentication, but without permissions, expecting 403 Forbidden
150
my $tx = $t->ua->build_tx(GET => "/api/v1/holds?borrowernumber=$borrowernumber");
151
$tx->req->cookies({name => 'CGISESSID', value => $session_nopermission->id});
152
$t->request_ok($tx) # no permission
153
  ->status_is(403);
154
$tx = $t->ua->build_tx(GET => "/api/v1/holds?borrowernumber=$borrowernumber");
155
$tx->req->cookies({name => 'CGISESSID', value => $session3->id});
156
$t->request_ok($tx) # reserveforothers permission
157
  ->status_is(403);
158
$tx = $t->ua->build_tx(POST => "/api/v1/holds" => json => $post_data );
159
$tx->req->cookies({name => 'CGISESSID', value => $session_nopermission->id});
160
$t->request_ok($tx) # no permission
161
  ->status_is(403);
162
$tx = $t->ua->build_tx(PUT => "/api/v1/holds/0" => json => $put_data );
163
$tx->req->cookies({name => 'CGISESSID', value => $session_nopermission->id});
164
$t->request_ok($tx) # no permission
165
  ->status_is(403);
166
$tx = $t->ua->build_tx(DELETE => "/api/v1/holds/0");
167
$tx->req->cookies({name => 'CGISESSID', value => $session_nopermission->id});
168
$t->request_ok($tx) # no permission
169
  ->status_is(403);
170
171
# Test endpoints with no permission, but accessing own object, expecting success
172
my $borrno_tmp = $post_data->{'borrowernumber'};
173
$post_data->{'borrowernumber'} = $nopermission->{'borrowernumber'};
174
$tx = $t->ua->build_tx(POST => "/api/v1/holds" => json => $post_data);
175
$tx->req->cookies({name => 'CGISESSID', value => $session_nopermission->id});
176
$t->request_ok($tx) # create hold to myself
177
  ->status_is(201)
178
  ->json_has('/reserve_id');
179
$post_data->{'borrowernumber'} = $borrno_tmp;
180
$tx = $t->ua->build_tx(GET => "/api/v1/holds?borrowernumber=".$nopermission-> { borrowernumber });
181
$tx->req->cookies({name => 'CGISESSID', value => $session_nopermission->id});
182
$t->request_ok($tx) # get my own holds
183
  ->status_is(200)
184
  ->json_is('/0/borrowernumber', $nopermission->{ borrowernumber })
185
  ->json_is('/0/biblionumber', $biblionumber)
186
  ->json_is('/0/itemnumber', $itemnumber)
187
  ->json_is('/0/expirationdate', $expirationdate)
188
  ->json_is('/0/branchcode', $branchcode);
189
190
# Test endpoints with permission, expecting success
191
$tx = $t->ua->build_tx(PUT => "/api/v1/holds/$reserve_id" => json => $put_data);
192
$tx->req->cookies({name => 'CGISESSID', value => $session3->id});
193
$t->request_ok($tx)
72
  ->status_is(200)
194
  ->status_is(200)
73
  ->json_is('/reserve_id', $reserve_id)
195
  ->json_is('/reserve_id', $reserve_id)
74
  ->json_is('/suspend_until', $suspend_until . ' 00:00:00')
196
  ->json_is('/suspend_until', $suspend_until . ' 00:00:00')
75
  ->json_is('/priority', 2);
197
  ->json_is('/priority', 2);
76
198
77
$t->delete_ok("/api/v1/holds/$reserve_id")
199
$tx = $t->ua->build_tx(DELETE => "/api/v1/holds/$reserve_id");
200
$tx->req->cookies({name => 'CGISESSID', value => $session3->id});
201
$t->request_ok($tx)
78
  ->status_is(200);
202
  ->status_is(200);
79
203
80
$t->put_ok("/api/v1/holds/$reserve_id" => json => $put_data)
204
$tx = $t->ua->build_tx(PUT => "/api/v1/holds/$reserve_id" => json => $put_data);
205
$tx->req->cookies({name => 'CGISESSID', value => $session3->id});
206
$t->request_ok($tx)
81
  ->status_is(404)
207
  ->status_is(404)
82
  ->json_has('/error');
208
  ->json_has('/error');
83
209
84
$t->delete_ok("/api/v1/holds/$reserve_id")
210
$tx = $t->ua->build_tx(DELETE => "/api/v1/holds/$reserve_id");
211
$tx->req->cookies({name => 'CGISESSID', value => $session3->id});
212
$t->request_ok($tx)
85
  ->status_is(404)
213
  ->status_is(404)
86
  ->json_has('/error');
214
  ->json_has('/error');
87
215
88
216
$tx = $t->ua->build_tx(GET => "/api/v1/holds?borrowernumber=".$borrower->borrowernumber);
89
$t->get_ok("/api/v1/holds?borrowernumber=$borrowernumber")
217
$tx->req->cookies({name => 'CGISESSID', value => $session2->id}); # get with borrowers flag
218
$t->request_ok($tx)
90
  ->status_is(200)
219
  ->status_is(200)
91
  ->json_is([]);
220
  ->json_is([]);
92
221
93
my $inexisting_borrowernumber = $borrowernumber2 + 1;
222
my $inexisting_borrowernumber = $borrowernumber2*2;
94
$t->get_ok("/api/v1/holds?borrowernumber=$inexisting_borrowernumber")
223
$tx = $t->ua->build_tx(GET => "/api/v1/holds?borrowernumber=$inexisting_borrowernumber");
224
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
225
$t->request_ok($tx)
95
  ->status_is(404)
226
  ->status_is(404)
96
  ->json_has('/error');
227
  ->json_has('/error');
97
228
Lines 101-127 $dbh->do(q{ Link Here
101
    VALUES (?, ?, ?, ?)
232
    VALUES (?, ?, ?, ?)
102
}, {}, '*', '*', '*', 1);
233
}, {}, '*', '*', '*', 1);
103
234
104
my $expirationdate = DateTime->now->add(days => 10)->ymd;
235
$tx = $t->ua->build_tx(DELETE => "/api/v1/holds/$reserve_id2");
105
my $post_data = {
236
$tx->req->cookies({name => 'CGISESSID', value => $session3->id});
106
    borrowernumber => int($borrowernumber),
237
$t->request_ok($tx)
107
    biblionumber => int($biblionumber),
238
  ->status_is(200);
108
    itemnumber => int($itemnumber),
239
109
    branchcode => $branchcode,
240
$tx = $t->ua->build_tx(POST => "/api/v1/holds" => json => $post_data);
110
    expirationdate => $expirationdate,
241
$tx->req->cookies({name => 'CGISESSID', value => $session3->id});
111
};
242
$t->request_ok($tx)
112
$t->post_ok("/api/v1/holds" => json => $post_data)
113
  ->status_is(201)
243
  ->status_is(201)
114
  ->json_has('/reserve_id');
244
  ->json_has('/reserve_id');
115
116
$reserve_id = $t->tx->res->json->{reserve_id};
245
$reserve_id = $t->tx->res->json->{reserve_id};
117
246
118
$t->get_ok("/api/v1/holds?borrowernumber=$borrowernumber")
247
$tx = $t->ua->build_tx(GET => "/api/v1/holds?borrowernumber=$borrowernumber");
248
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
249
$t->request_ok($tx)
119
  ->status_is(200)
250
  ->status_is(200)
120
  ->json_is('/0/reserve_id', $reserve_id)
251
  ->json_is('/0/reserve_id', $reserve_id)
121
  ->json_is('/0/expirationdate', $expirationdate)
252
  ->json_is('/0/expirationdate', $expirationdate)
122
  ->json_is('/0/branchcode', $branchcode);
253
  ->json_is('/0/branchcode', $branchcode);
123
254
124
$t->post_ok("/api/v1/holds" => json => $post_data)
255
$tx = $t->ua->build_tx(POST => "/api/v1/holds" => json => $post_data);
256
$tx->req->cookies({name => 'CGISESSID', value => $session3->id});
257
$t->request_ok($tx)
125
  ->status_is(403)
258
  ->status_is(403)
126
  ->json_like('/error', qr/tooManyReserves/);
259
  ->json_like('/error', qr/tooManyReserves/);
127
260
(-)a/t/db_dependent/api/v1/patrons.t (-5 / +28 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 10;
20
use Test::More tests => 16;
21
use Test::Mojo;
21
use Test::Mojo;
22
use t::lib::TestBuilder;
22
use t::lib::TestBuilder;
23
23
Lines 42-48 my $borrower = $builder->build({ Link Here
42
    source => 'Borrower',
42
    source => 'Borrower',
43
    value => {
43
    value => {
44
        branchcode   => $branchcode,
44
        branchcode   => $branchcode,
45
        categorycode => $categorycode
45
        categorycode => $categorycode,
46
        flags        => 0
46
    }
47
    }
47
});
48
});
48
49
Lines 52-57 $t->get_ok('/api/v1/patrons') Link Here
52
$t->get_ok("/api/v1/patrons/" . $borrower->{ borrowernumber })
53
$t->get_ok("/api/v1/patrons/" . $borrower->{ borrowernumber })
53
  ->status_is(401);
54
  ->status_is(401);
54
55
56
my $session = C4::Auth::get_session('');
57
$session->param('number', $borrower->{ borrowernumber });
58
$session->param('id', $borrower->{ userid });
59
$session->param('ip', '127.0.0.1');
60
$session->param('lasttime', time());
61
$session->flush;
62
63
my $tx = $t->ua->build_tx(GET => '/api/v1/patrons');
64
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
65
$t->request_ok($tx)
66
  ->status_is(403);
67
68
$tx = $t->ua->build_tx(GET => "/api/v1/patrons/" . ($borrower->{ borrowernumber }-1));
69
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
70
$t->request_ok($tx)
71
  ->status_is(403);
72
73
# User without permissions, but is the owner of the object
74
$tx = $t->ua->build_tx(GET => "/api/v1/patrons/" . ($borrower->{ borrowernumber }));
75
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
76
$t->request_ok($tx)
77
  ->status_is(200);
78
55
my $loggedinuser = $builder->build({
79
my $loggedinuser = $builder->build({
56
    source => 'Borrower',
80
    source => 'Borrower',
57
    value => {
81
    value => {
Lines 61-74 my $loggedinuser = $builder->build({ Link Here
61
    }
85
    }
62
});
86
});
63
87
64
my $session = C4::Auth::get_session('');
88
$session = C4::Auth::get_session('');
65
$session->param('number', $loggedinuser->{ borrowernumber });
89
$session->param('number', $loggedinuser->{ borrowernumber });
66
$session->param('id', $loggedinuser->{ userid });
90
$session->param('id', $loggedinuser->{ userid });
67
$session->param('ip', '127.0.0.1');
91
$session->param('ip', '127.0.0.1');
68
$session->param('lasttime', time());
92
$session->param('lasttime', time());
69
$session->flush;
93
$session->flush;
70
94
71
my $tx = $t->ua->build_tx(GET => '/api/v1/patrons');
95
$tx = $t->ua->build_tx(GET => '/api/v1/patrons');
72
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
96
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
73
$tx->req->env({REMOTE_ADDR => '127.0.0.1'});
97
$tx->req->env({REMOTE_ADDR => '127.0.0.1'});
74
$t->request_ok($tx)
98
$t->request_ok($tx)
75
- 

Return to bug 14868