Bugzilla – Attachment 117109 Details for
Bug 20028
Export all patron related personal data in one package
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 20028: Add Patron Export REST controller
Bug-20028-Add-Patron-Export-REST-controller.patch (text/plain), 10.29 KB, created by
Lari Taskula
on 2021-02-20 01:57:30 UTC
(
hide
)
Description:
Bug 20028: Add Patron Export REST controller
Filename:
MIME Type:
Creator:
Lari Taskula
Created:
2021-02-20 01:57:30 UTC
Size:
10.29 KB
patch
obsolete
>From 265448b20f6dd1f8701eea9e29c47fd90f9b74be Mon Sep 17 00:00:00 2001 >From: Lari Taskula <lari.taskula@hypernova.fi> >Date: Fri, 19 Feb 2021 11:52:48 +0000 >Subject: [PATCH] Bug 20028: Add Patron Export REST controller > >This patch adds a REST controller for patron data exporting. > >To test: >1. prove t/db_dependent/api/v1/patrons_export.t > >Sponsored-by: The National Library of Finland >--- > Koha/REST/V1/Patrons/Export.pm | 114 +++++++++++++++ > t/db_dependent/api/v1/patrons_export.t | 184 ++++++++++++++++++++++++- > 2 files changed, 297 insertions(+), 1 deletion(-) > create mode 100644 Koha/REST/V1/Patrons/Export.pm > >diff --git a/Koha/REST/V1/Patrons/Export.pm b/Koha/REST/V1/Patrons/Export.pm >new file mode 100644 >index 0000000000..bca43420b1 >--- /dev/null >+++ b/Koha/REST/V1/Patrons/Export.pm >@@ -0,0 +1,114 @@ >+package Koha::REST::V1::Patrons::Export; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use Koha::Patrons; >+ >+use Scalar::Util qw(blessed); >+use Try::Tiny; >+ >+=head1 NAME >+ >+Koha::REST::V1::Patrons::Export >+ >+=head1 API >+ >+=head2 Methods >+ >+=head3 get >+ >+Controller method that gets patron's related data, permission driven >+ >+=cut >+ >+sub get { >+ >+ my $c = shift->openapi->valid_input or return; >+ >+ my $patron = Koha::Patrons->find( $c->validation->param('patron_id') ); >+ my $body = $c->validation->param('body'); >+ >+ return try { >+ >+ unless ($patron) { >+ return $c->render( status => 404, openapi => { >+ error => "Patron not found." >+ } ); >+ } >+ >+ unless ( C4::Context->preference('AllowGDPRPatronExport') ) { >+ return $c->render( >+ status => 403, >+ openapi => { error => "Configuration prevents patron data export" } >+ ); >+ } >+ >+ my $export = $patron->export; >+ >+ return $c->render( status => 200, openapi => $export ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ }; >+} >+ >+=head3 get_public >+ >+Controller method that gets patron's related data, for unprivileged users >+ >+=cut >+ >+sub get_public { >+ >+ my $c = shift->openapi->valid_input or return; >+ >+ my $body = $c->validation->param('body'); >+ my $patron_id = $c->validation->param('patron_id'); >+ >+ return try { >+ >+ unless ( C4::Context->preference('AllowGDPRPatronExport') ) { >+ return $c->render( >+ status => 403, >+ openapi => { error => "Configuration prevents patron data export" } >+ ); >+ } >+ >+ my $user = $c->stash('koha.user'); >+ >+ unless ( $user->borrowernumber == $patron_id ) { >+ return $c->render( >+ status => 403, >+ openapi => { >+ error => "Accessing other patron's data is forbidden" >+ } >+ ); >+ } >+ >+ my $export = $user->export; >+ >+ return $c->render( status => 200, openapi => $export ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ }; >+} >+ >+1; >diff --git a/t/db_dependent/api/v1/patrons_export.t b/t/db_dependent/api/v1/patrons_export.t >index 3e5e35e5b1..86a3d9111f 100755 >--- a/t/db_dependent/api/v1/patrons_export.t >+++ b/t/db_dependent/api/v1/patrons_export.t >@@ -17,7 +17,7 @@ > > use Modern::Perl; > >-use Test::More tests => 1; >+use Test::More tests => 3; > > use Test::Mojo; > >@@ -29,8 +29,80 @@ my $builder = t::lib::TestBuilder->new; > > t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); > >+# this is recommended because generation of test data may take a long time and >+# Mojolicious would otherwise die with "Premature connection close" >+$ENV{MOJO_INACTIVITY_TIMEOUT} = 120; >+ > my $t = Test::Mojo->new('Koha::REST::V1'); > >+subtest 'get() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ unauthorized_access_tests('GET', -1, undef); >+ $schema->storage->txn_rollback; >+ >+ $schema->storage->txn_begin; >+ >+ my $generated = generate_test_data(); >+ my $patron = $generated->{patron}; >+ my $test_objects = $generated->{test_objects}; >+ >+ my $password = '12345'; >+ my $librarian = $builder->build_object({ >+ class => 'Koha::Patrons', >+ value => { flags => 2**4 } # borrowers flag = 4 >+ }); >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ t::lib::Mocks::mock_preference( 'AllowGDPRPatronExport', 1 ); >+ my $ret = $t->get_ok("//$userid:$password@/api/v1/patrons/" . $patron->id >+ . '/export') >+ ->status_is(200) >+ ->json_is($test_objects); >+ >+ t::lib::Mocks::mock_preference( 'AllowGDPRPatronExport', 0 ); >+ $ret = $t->get_ok("//$userid:$password@/api/v1/patrons/" >+ . $patron->id . '/export') >+ ->status_is(403) >+ ->json_is('/error', 'Configuration prevents patron data export'); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'get_public() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ unauthorized_access_tests('GET', -1, undef, 1); >+ $schema->storage->txn_rollback; >+ >+ $schema->storage->txn_begin; >+ >+ my $generated = generate_test_data(); >+ my $patron = $generated->{patron}; >+ my $test_objects = $generated->{test_objects}; >+ my $userid = $patron->userid; >+ my $password = $patron->{cleartext_password}; >+ >+ t::lib::Mocks::mock_preference( 'AllowGDPRPatronExport', 1 ); >+ my $ret = $t->get_ok("//$userid:$password@/api/v1/public/patrons/" >+ . $patron->id . '/export') >+ ->status_is(200) >+ ->json_is($test_objects); >+ >+ t::lib::Mocks::mock_preference( 'AllowGDPRPatronExport', 0 ); >+ $ret = $t->get_ok("//$userid:$password@/api/v1/public/patrons/" >+ . $patron->id . '/export') >+ ->status_is(403) >+ ->json_is('/error', 'Configuration prevents patron data export'); >+ >+ $schema->storage->txn_rollback; >+}; >+ > subtest 'validate patrons_export OpenAPI definition' => sub { > > my @related_sources = _get_related_sources(); >@@ -78,3 +150,113 @@ sub _get_related_sources { > my @sorted = sort keys %$sources; > return @sorted; > } >+ >+sub generate_test_data { >+ my @related_sources = _get_related_sources(); >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ } >+ ); >+ >+ my $password = '12345'; >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ $patron->discard_changes; >+ $patron->{cleartext_password} = $password; >+ my $test_objects = { >+ 'Borrower' => $patron->unblessed, >+ }; >+ >+ my $limit_data = 5; # build this many random test objects >+ my $generated_data = 0; >+ >+ foreach my $src ( @related_sources ) { >+ next if $src eq 'Borrower'; # Borrower is a hashref, >+ $test_objects->{$src} = []; # the others are arrayrefs >+ } >+ >+ my $result_source = Koha::Patron->new->_result()->result_source; >+ foreach my $rel ( Koha::Patron->new->_result()->relationships() ) { >+ if ($generated_data >= $limit_data) { >+ last; >+ } >+ >+ my $related_source = $result_source->related_source( $rel ); >+ my $source_name = $related_source->source_name; >+ >+ my $info = $result_source->relationship_info( $rel ); >+ >+ # We are not interested in the "belongs_to" relationship of borrowers. >+ # These are tables like branches, categories and sms_provider. >+ if ( $info->{'attrs'}->{'is_depends_on'} ) { >+ next; >+ } >+ >+ ( my $rel_col = (keys %{$info->{'cond'}})[0] ) =~ s/^foreign\.//; >+ >+ # Generate test data into related tables >+ my $built; >+ if ( $related_source->result_class->can('koha_objects_class') ) { >+ $built = $builder->build_object( >+ { >+ class => $related_source->result_class->koha_objects_class, >+ value => { $rel_col => $patron->borrowernumber } >+ } >+ ); >+ $built = $built->unblessed; >+ } else { >+ $built = $builder->build( >+ { >+ source => $source_name, >+ value => { $rel_col => $patron->borrowernumber } >+ } >+ ); >+ } >+ push @{ $test_objects->{$related_source->source_name} }, $built; >+ $generated_data++; >+ } >+ >+ # Generate test data into a random table. Just one because test data >+ # generation takes so long. >+ $builder->build( { >+ source => (keys %$test_objects)[rand keys %$test_objects] >+ } ); >+ >+ return { >+ patron => $patron, >+ test_objects => $test_objects >+ }; >+} >+ >+# Centralized tests for 401s and 403s assuming the endpoint requires >+# borrowers flag for access >+sub unauthorized_access_tests { >+ my ($verb, $patron_id, $json, $public) = @_; >+ >+ my $endpoint = '/api/v1/' . ( $public ? 'public/' : '' ) . 'patrons'; >+ $endpoint .= ($patron_id) ? "/$patron_id/export" : ''; >+ >+ subtest 'unauthorized access tests' => sub { >+ plan tests => 5; >+ >+ my $verb_ok = lc($verb) . '_ok'; >+ >+ $t->$verb_ok($endpoint => json => $json) >+ ->status_is(401); >+ >+ my $unauthorized_patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ my $password = "12345"; >+ $unauthorized_patron->set_password( >+ { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $unauthorized_patron->userid; >+ >+ $t->$verb_ok( "//$unauth_userid:$password\@$endpoint" => json => $json ) >+ ->status_is(403) >+ ->json_has('/required_permissions'); >+ }; >+} >-- >2.25.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 20028
:
75622
|
78095
|
79376
|
79381
|
116344
|
116604
|
117106
|
117107
|
117108
|
117109
|
119968
|
119969
|
119970
|
119971
|
119972
|
119974
|
119975
|
119976
|
119977
|
120844
|
120845
|
120846
|
120847
|
124156
|
124157
|
124158
|
124159
|
124160
|
124161
|
124164
|
124165
|
124166
|
124167
|
124168