From fe175f885762c1d6bfda50aaa5ab164c29c4bf26 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Mon, 23 Oct 2023 16:19:18 +0100 Subject: [PATCH] Bug 29523: (follow-up) Comprehensive tests for redaction Signed-off-by: Martin Renvoize --- api/v1/swagger/swagger.yaml | 25 +++++++++++-- t/db_dependent/Koha/Object.t | 70 +++++++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 351f796c56e..a13f277830b 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -767,9 +767,20 @@ info: name: Koha Development Team url: https://koha-community.org/ description: | - ## Introduction - - This API is documented in **OpenAPI format**. + ## Background + + The API supports two sets of endpoints, one targetted at library staff and the other at at library users. + + Those endpoints under the `/public` path are aimed at delivering functionality tailored to library users and offer + a more restricted set of functions, overrides and data in thier responses for data privacy and library policy + reasons. Many of these endpoints do not require authentication for fetching public data, though an authenticated + session will expose additional options and allow users to see more data where it is part of their own record. + + All other endpoints are targetted at the staff interface level and allow for additional functionality and a more + unrestricted view of data. These endpoints, however, have a level of redaction built in for resources that the + api consumer should not have access to. For example, user data for users who do not belong to the same library + or library group of your api user will be reduced to just minimum neccesary for a valid response. Object keys will + be consistent for all responses, but their values may be removed depending on access. ## Authentication @@ -782,6 +793,14 @@ info: Both _Basic authentication_ and the _OAuth2_ flow, need to be enabled by system preferences. + ## Authorization + + The API uses existing user profiles to restrict access to resources based on user permissions and the library the + API user is assigned to. This may result, at times, in resources being returned in a redacted form with all keys + present but sensative values nulled. + + We do not yet support OAuth Scopes or the Authorization Code grant flow. + ## Errors The API uses standard HTTP status codes to indicate the success or failure diff --git a/t/db_dependent/Koha/Object.t b/t/db_dependent/Koha/Object.t index ecf0bed621a..035d9cddefc 100755 --- a/t/db_dependent/Koha/Object.t +++ b/t/db_dependent/Koha/Object.t @@ -397,7 +397,7 @@ subtest "to_api() tests" => sub { 'Koha::Exceptions::Object::MethodNotCoveredByTests', 'Unknown method exception thrown if is_count not specified'; - subtest 'unprivileged request tests' => sub { + subtest 'public request tests' => sub { my @all_attrs = Koha::Libraries->columns(); my $public_attrs = { map { $_ => 1 } @{ Koha::Library->public_read_list() } }; @@ -535,16 +535,21 @@ subtest "to_api() tests" => sub { $schema->storage->txn_rollback; }; - subtest 'accessible usage tests' => sub { + subtest 'unprivileged requests redaction tests' => sub { - plan tests => 2; + my @all_attrs = Koha::Patrons->columns(); + my $unredacted_attrs = { map { $_ => 1 } @{ Koha::Patron->unredact_list() } }; + my $mapping = Koha::Patron->to_api_mapping; + my @mapped_to_null = grep { !defined( $mapping->{$_} ) } keys %{$mapping}; + + plan tests => ( scalar @all_attrs * 3 ) - scalar @mapped_to_null; $schema->storage->txn_begin; my $library_1 = $builder->build_object( { class => 'Koha::Libraries' } ); my $library_2 = $builder->build_object( { class => 'Koha::Libraries' } ); - my $patron = $builder->build_object( + my $api_consumer = $builder->build_object( { class => 'Koha::Patrons', value => { @@ -554,20 +559,53 @@ subtest "to_api() tests" => sub { } ); - my $patron_1 = $builder->build_object( - { class => 'Koha::Patrons', value => { branchcode => $library_1->id } } - ); - my $patron_2 = $builder->build_object( - { class => 'Koha::Patrons', value => { branchcode => $library_2->id } } - ); + my $patron_1 = + $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library_1->id } } ); + my $patron_2 = + $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library_2->id } } ); - t::lib::Mocks::mock_userenv( { patron => $patron } ); + t::lib::Mocks::mock_userenv( { patron => $api_consumer } ); - is( - $patron_1->to_api( { user => $patron } )->{firstname}, $patron_1->firstname, - 'Returns unredacted object hash' - ); - is( $patron_2->to_api( { user => $patron } )->{firstname}, undef, 'Returns redacted object hash' ); + my $visible_user = $patron_1->to_api( { user => $api_consumer } ); + my $redacted_user = $patron_2->to_api( { user => $api_consumer } ); + + foreach my $attr (@all_attrs) { + my $mapped = exists $mapping->{$attr} ? $mapping->{$attr} : $attr; + if ( defined($mapped) ) { + ok( + exists $visible_user->{$mapped}, + "Mapped attribute '$attr' is present" + ); + if ( exists $unredacted_attrs->{$attr} ) { + is( + $visible_user->{$mapped}, $patron_1->TO_JSON->{$attr}, + "Unredacted attribute '$attr' is visible when user is accessible" + ); + is( + $redacted_user->{$mapped}, $patron_2->TO_JSON->{$attr}, + "Unredacted attribute '$attr' is visible when user is inaccessible" + ); + } else { + is( + $visible_user->{$mapped}, $patron_1->TO_JSON->{$attr}, + "Redacted attribute '$attr' is visible when user is accessible" + ); + is( + $redacted_user->{$mapped}, undef, + "Redacted attribute '$attr' is undefined when user is inaccessible" + ); + } + } else { + ok( + !exists $visible_user->{$attr}, + "Mapped to null attribute '$attr' is not present when accessible" + ); + ok( + !exists $redacted_user->{$attr}, + "Mapped to null attribute '$attr' is not present when inaccessible" + ); + } + } $schema->storage->txn_rollback; }; -- 2.41.0