Bug 28772: Fix auth_authenticate_api_request.t
[koha.git] / t / db_dependent / api / v1 / advanced_editor_macros.t
1 #!/usr/bin/env perl
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 3 of the License, or (at your option) any later
8 # version.
9 #
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with Koha; if not, see <http://www.gnu.org/licenses>.
16
17 use Modern::Perl;
18
19 use Test::More tests => 5;
20 use Test::Mojo;
21 use Test::Warn;
22
23 use t::lib::TestBuilder;
24 use t::lib::Mocks;
25
26 use Koha::AdvancedEditorMacros;
27 use Koha::Database;
28
29 my $schema  = Koha::Database->new->schema;
30 my $builder = t::lib::TestBuilder->new;
31
32 t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
33
34 my $t = Test::Mojo->new('Koha::REST::V1');
35
36 $schema->storage->txn_begin;
37
38 subtest 'list() tests' => sub {
39     plan tests => 8;
40
41
42     my $patron_1 = $builder->build_object({
43         class => 'Koha::Patrons',
44         value => { flags => 9 }
45     });
46     my $patron_2 = $builder->build_object({
47         class => 'Koha::Patrons',
48     });
49     my $password = 'thePassword123';
50     $patron_1->set_password({ password => $password, skip_validation => 1 });
51     my $userid = $patron_1->userid;
52
53     # Create test context
54     my $macro_1 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
55         {
56             name => 'Test1',
57             macro => 'delete 100',
58             borrowernumber => $patron_1->borrowernumber,
59             shared => 0,
60         }
61     });
62     my $macro_2 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
63         {
64             name => 'Test2',
65             macro => 'delete 100',
66             borrowernumber => $patron_1->borrowernumber,
67             shared => 1,
68         }
69     });
70     my $macro_3 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
71         {
72             name => 'Test3',
73             macro => 'delete 100',
74             borrowernumber => $patron_2->borrowernumber,
75             shared => 0,
76         }
77     });
78     my $macro_4 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
79         {
80             name => 'Test4',
81             macro => 'delete 100',
82             borrowernumber => $patron_2->borrowernumber,
83             shared => 1,
84         }
85     });
86
87     my $macros_index = Koha::AdvancedEditorMacros->search({ -or => { shared => 1, borrowernumber => $patron_1->borrowernumber } })->count-1;
88     ## Authorized user tests
89     # Make sure we are returned with the correct amount of macros
90     $t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros" )
91       ->status_is( 200, 'SWAGGER3.2.2' )
92       ->json_has('/' . $macros_index . '/macro_id')
93       ->json_hasnt('/' . ($macros_index + 1) . '/macro_id');
94
95     subtest 'query parameters' => sub {
96
97         plan tests => 15;
98         $t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?name=" . $macro_2->name)
99           ->status_is(200)
100           ->json_is( [ $macro_2->to_api ] );
101         $t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?name=" . $macro_3->name)
102           ->status_is(200)
103           ->json_is( [ ] );
104         $t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?macro_text=delete%20100")
105           ->status_is(200)
106           ->json_is( [ $macro_1->to_api, $macro_2->to_api, $macro_4->to_api ] );
107         $t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?patron_id=" . $patron_1->borrowernumber)
108           ->status_is(200)
109           ->json_is( [ $macro_1->to_api, $macro_2->to_api ] );
110         $t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?shared=1")
111           ->status_is(200)
112           ->json_is( [ $macro_2->to_api, $macro_4->to_api ] );
113     };
114
115     # Warn on unsupported query parameter
116     $t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros?macro_blah=blah" )
117       ->status_is(400)
118       ->json_is( [{ path => '/query/macro_blah', message => 'Malformed query string'}] );
119
120 };
121
122 subtest 'get() tests' => sub {
123
124     plan tests => 15;
125
126     my $patron = $builder->build_object({
127         class => 'Koha::Patrons',
128         value => { flags => 1 }
129     });
130     my $password = 'thePassword123';
131     $patron->set_password({ password => $password, skip_validation => 1 });
132     my $userid = $patron->userid;
133
134     my $macro_1 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
135             shared => 1,
136         }
137     });
138     my $macro_2 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
139             shared => 0,
140         }
141     });
142     my $macro_3 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
143             borrowernumber => $patron->borrowernumber,
144             shared => 0,
145         }
146     });
147
148     $t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/" . $macro_1->id )
149       ->status_is( 403, 'Cannot get a shared macro via regular endpoint' )
150       ->json_is( '/error' => 'This macro is shared, you must access it via advanced_editor/macros/shared' );
151
152     $t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/shared/" . $macro_1->id )
153       ->status_is( 200, 'Can get a shared macro via shared endpoint' )
154       ->json_is( $macro_1->to_api );
155
156     $t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/" . $macro_2->id )
157       ->status_is( 403, 'Cannot access another users macro' )
158       ->json_is( '/error' => 'You do not have permission to access this macro' );
159
160     $t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/" . $macro_3->id )
161       ->status_is( 200, 'Can get your own private macro' )
162       ->json_is( $macro_3->to_api );
163
164     my $non_existent_code = $macro_1->id;
165     $macro_1->delete;
166
167     $t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/" . $non_existent_code )
168       ->status_is(404)
169       ->json_is( '/error' => 'Macro not found' );
170
171 };
172
173 subtest 'add() tests' => sub {
174
175     plan tests => 24;
176
177     my $authorized_patron = $builder->build_object({
178         class => 'Koha::Patrons',
179         value => { flags => 0 }
180     });
181     $builder->build({
182         source => 'UserPermission',
183         value  => {
184             borrowernumber => $authorized_patron->borrowernumber,
185             module_bit     => 9,
186             code           => 'advanced_editor',
187         },
188     });
189
190     my $password = 'thePassword123';
191     $authorized_patron->set_password({ password => $password, skip_validation => 1 });
192     my $auth_userid = $authorized_patron->userid;
193
194     my $unauthorized_patron = $builder->build_object({
195         class => 'Koha::Patrons',
196         value => { flags => 0 }
197     });
198     $unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
199     my $unauth_userid = $unauthorized_patron->userid;
200
201     my $macro = $builder->build_object({
202         class => 'Koha::AdvancedEditorMacros',
203         value => { shared => 0 }
204     });
205     my $macro_values = $macro->to_api;
206     delete $macro_values->{macro_id};
207     $macro->delete;
208
209     # Unauthorized attempt to write
210     $t->post_ok( "//$unauth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
211       ->status_is(403);
212
213     # Authorized attempt to write invalid data
214     my $macro_with_invalid_field = { %$macro_values };
215     $macro_with_invalid_field->{'big_mac_ro'} = 'Mac attack';
216
217     $t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_with_invalid_field )
218       ->status_is(400)
219       ->json_is(
220         "/errors" => [
221             {
222                 message => "Properties not allowed: big_mac_ro.",
223                 path    => "/body"
224             }
225         ]
226     );
227
228     # Authorized attempt to write
229     $t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
230       ->status_is( 201, 'SWAGGER3.2.1' )
231       ->json_has( '/macro_id', 'We generated a new id' )
232       ->json_is( '/name' => $macro_values->{name}, 'The name matches what we supplied' )
233       ->json_is( '/macro_text' => $macro_values->{macro_text}, 'The text matches what we supplied' )
234       ->json_is( '/patron_id' => $macro_values->{patron_id}, 'The borrower matches the borrower who submitted' )
235       ->json_is( '/shared' => Mojo::JSON->false, 'The macro is not shared' )
236       ->header_like( Location => qr|^\/api\/v1\/advanced_editor/macros\/d*|, 'Correct location' );
237
238     # save the library_id
239     my $macro_id = 999;
240
241     # Authorized attempt to create with existing id
242     $macro_values->{macro_id} = $macro_id;
243
244     $t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
245       ->status_is(400)
246       ->json_is( '/errors' => [
247             {
248                 message => "Read-only.",
249                 path   => "/body/macro_id"
250             }
251         ]
252     );
253
254     $macro_values->{shared} = Mojo::JSON->true;
255     delete $macro_values->{macro_id};
256
257     # Unauthorized attempt to write a shared macro on private endpoint
258     $t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
259       ->status_is(403);
260     # Unauthorized attempt to write a private macro on shared endpoint
261     $t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared" => json => $macro_values )
262       ->status_is(403);
263
264     $builder->build({
265         source => 'UserPermission',
266         value  => {
267             borrowernumber => $authorized_patron->borrowernumber,
268             module_bit     => 9,
269             code           => 'create_shared_macros',
270         },
271     });
272
273     # Authorized attempt to write a shared macro on private endpoint
274     $t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
275       ->status_is(403);
276
277     # Authorized attempt to write a shared macro on shared endpoint
278     $t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared" => json => $macro_values )
279       ->status_is(201);
280
281 };
282
283 subtest 'update() tests' => sub {
284     plan tests => 32;
285
286     my $authorized_patron = $builder->build_object({
287         class => 'Koha::Patrons',
288         value => { flags => 0 }
289     });
290     $builder->build({
291         source => 'UserPermission',
292         value  => {
293             borrowernumber => $authorized_patron->borrowernumber,
294             module_bit     => 9,
295             code           => 'advanced_editor',
296         },
297     });
298
299     my $password = 'thePassword123';
300     $authorized_patron->set_password({ password => $password, skip_validation => 1 });
301     my $auth_userid = $authorized_patron->userid;
302
303     my $unauthorized_patron = $builder->build_object({
304         class => 'Koha::Patrons',
305         value => { flags => 0 }
306     });
307     $unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
308     my $unauth_userid = $unauthorized_patron->userid;
309
310     my $macro = $builder->build_object({
311         class => 'Koha::AdvancedEditorMacros',
312         value => { borrowernumber => $authorized_patron->borrowernumber, shared => 0 }
313     });
314     my $macro_2 = $builder->build_object({
315         class => 'Koha::AdvancedEditorMacros',
316         value => { borrowernumber => $unauthorized_patron->borrowernumber, shared => 0 }
317     });
318     my $macro_id = $macro->id;
319     my $macro_2_id = $macro_2->id;
320     my $macro_values = $macro->to_api;
321     delete $macro_values->{macro_id};
322
323     # Unauthorized attempt to update
324     $t->put_ok( "//$unauth_userid:$password@/api/v1/advanced_editor/macros/$macro_id"
325                     => json => { name => 'New unauthorized name change' } )
326       ->status_is(403);
327
328     # Attempt partial update on a PUT
329     my $macro_with_missing_field = {
330         name => "Call it macro-roni",
331     };
332
333     $t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id" => json => $macro_with_missing_field )
334       ->status_is(400)
335       ->json_has( "/errors" =>
336           [ { message => "Missing property.", path => "/body/macro_text" } ]
337       );
338
339     my $macro_update = {
340         name => "Macro-update",
341         macro_text => "delete 100",
342         patron_id => $authorized_patron->borrowernumber,
343         shared => Mojo::JSON->false,
344     };
345
346     my $test = $t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id" => json => $macro_update )
347       ->status_is(200, 'Authorized user can update a macro')
348       ->json_is( '/macro_id' => $macro_id, 'We get the id back' )
349       ->json_is( '/name' => $macro_update->{name}, 'We get the name back' )
350       ->json_is( '/macro_text' => $macro_update->{macro_text}, 'We get the text back' )
351       ->json_is( '/patron_id' => $macro_update->{patron_id}, 'We get the patron_id back' )
352       ->json_is( '/shared' => $macro_update->{shared}, 'It should still not be shared' );
353
354     # Now try to make the macro shared
355     $macro_update->{shared} = Mojo::JSON->true;
356
357     $t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_id" => json => $macro_update )
358       ->status_is(403, 'Cannot make your macro shared on private endpoint');
359     $t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_id" => json => $macro_update )
360       ->status_is(403, 'Cannot make your macro shared without permission');
361
362     $builder->build({
363         source => 'UserPermission',
364         value  => {
365             borrowernumber => $authorized_patron->borrowernumber,
366             module_bit     => 9,
367             code           => 'create_shared_macros',
368         },
369     });
370
371     $t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id" => json => $macro_update )
372       ->status_is(403, 'Cannot make your macro shared on the private endpoint');
373
374     $t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_id" => json => $macro_update )
375       ->status_is(200, 'Can update macro to shared with permission')
376       ->json_is( '/macro_id' => $macro_id, 'We get back the id' )
377       ->json_is( '/name' => $macro_update->{name}, 'We get back the name' )
378       ->json_is( '/macro_text' => $macro_update->{macro_text}, 'We get back the text' )
379       ->json_is( '/patron_id' => $macro_update->{patron_id}, 'We get back our patron id' )
380       ->json_is( '/shared' => Mojo::JSON->true, 'It is shared' );
381
382     # Authorized attempt to write invalid data
383     my $macro_with_invalid_field = { %$macro_update };
384     $macro_with_invalid_field->{'big_mac_ro'} = 'Mac attack';
385
386     $t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id" => json => $macro_with_invalid_field )
387       ->status_is(400)
388       ->json_is(
389         "/errors" => [
390             {
391                 message => "Properties not allowed: big_mac_ro.",
392                 path    => "/body"
393             }
394         ]
395     );
396
397     my $non_existent_macro = $builder->build_object({class => 'Koha::AdvancedEditorMacros'});
398     my $non_existent_code = $non_existent_macro->id;
399     $non_existent_macro->delete;
400
401     $t->put_ok("//$auth_userid:$password@/api/v1/advanced_editor/macros/$non_existent_code" => json => $macro_update)
402       ->status_is(404);
403
404     $t->put_ok("//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_2_id" => json => $macro_update)
405       ->status_is(403, "Cannot update other borrowers private macro");
406 };
407
408 subtest 'delete() tests' => sub {
409     plan tests => 12;
410
411     my $authorized_patron = $builder->build_object({
412         class => 'Koha::Patrons',
413         value => { flags => 0 }
414     });
415     $builder->build({
416         source => 'UserPermission',
417         value  => {
418             borrowernumber => $authorized_patron->borrowernumber,
419             module_bit     => 9,
420             code           => 'advanced_editor',
421         },
422     });
423
424     my $password = 'thePassword123';
425     $authorized_patron->set_password({ password => $password, skip_validation => 1 });
426     my $auth_userid = $authorized_patron->userid;
427
428     my $unauthorized_patron = $builder->build_object({
429         class => 'Koha::Patrons',
430         value => { flags => 0 }
431     });
432     $unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
433     my $unauth_userid = $unauthorized_patron->userid;
434
435     my $macro = $builder->build_object({
436         class => 'Koha::AdvancedEditorMacros',
437         value => { borrowernumber => $authorized_patron->borrowernumber, shared => 0 }
438     });
439     my $macro_2 = $builder->build_object({
440         class => 'Koha::AdvancedEditorMacros',
441         value => { borrowernumber => $unauthorized_patron->borrowernumber, shared => 0 }
442     });
443     my $macro_id = $macro->id;
444     my $macro_2_id = $macro_2->id;
445
446     # Unauthorized attempt to delete
447     $t->delete_ok( "//$unauth_userid:$password@/api/v1/advanced_editor/macros/$macro_2_id")
448       ->status_is(403, "Cannot delete macro without permission");
449
450     $t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id")
451       ->status_is( 204, 'Can delete macro with permission');
452
453     $t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_2_id")
454       ->status_is(403, 'Cannot delete other users macro with permission');
455
456     $macro_2->shared(1)->store();
457
458     $t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_2_id")
459       ->status_is(403, 'Cannot delete other users shared macro without permission');
460
461     $builder->build({
462         source => 'UserPermission',
463         value  => {
464             borrowernumber => $authorized_patron->borrowernumber,
465             module_bit     => 9,
466             code           => 'delete_shared_macros',
467         },
468     });
469     $t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_2_id")
470       ->status_is(403, 'Cannot delete other users shared macro with permission on private endpoint');
471     $t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_2_id")
472       ->status_is(204, 'Can delete other users shared macro with permission');
473
474 };
475
476 $schema->storage->txn_rollback;