Bugzilla – Attachment 60839 Details for
Bug 18137
Migrate from Mojolicious::Plugin::Swagger2 to Mojolicious::Plugin::OpenAPI
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 18137: Migrate from Swagger2 to Mojolicious::Plugin::OpenAPI
Bug-18137-Migrate-from-Swagger2-to-MojoliciousPlug.patch (text/plain), 23.17 KB, created by
Olli-Antti Kivilahti
on 2017-03-06 11:43:40 UTC
(
hide
)
Description:
Bug 18137: Migrate from Swagger2 to Mojolicious::Plugin::OpenAPI
Filename:
MIME Type:
Creator:
Olli-Antti Kivilahti
Created:
2017-03-06 11:43:40 UTC
Size:
23.17 KB
patch
obsolete
>From 2dd8b58e6f9584d2945bed5b08308adf56363ddb Mon Sep 17 00:00:00 2001 >From: Lari Taskula <lari.taskula@jns.fi> >Date: Fri, 17 Feb 2017 14:36:36 +0200 >Subject: [PATCH] Bug 18137: Migrate from Swagger2 to > Mojolicious::Plugin::OpenAPI > >This patch migrates from Swagger2 to Mojolicious::Plugin::OpenAPI as Swagger2 is >no longer actively maintained. > >This migration involves some minor changes to our Swagger specification documents >and to controllers. Each operation is migrated in following patches separately. >Please see Mojolicious::Plugin::OpenAPI and its tutorial for more documentation. > >The patch also refactors some API authentication -related code by taking advantage >of Koha::Exceptions. Authentication is now handled via Mojolicious's "under->to" >functionality. The actual authentication & authorization checks are moved to >Koha::REST::V1::Auth. Added a HTTP 503 response for when database update is >required, instead of returning an authentication failure as before. > >To test: >1. prove t/db_dependent/api/v1/auth.t > >Signed-off-by: Olli-Antti Kivilahti <olli-antti.kivilahti@jns.fi> >--- > Koha/REST/V1.pm | 241 +------------------------------ > Koha/REST/V1/Auth.pm | 329 +++++++++++++++++++++++++++++++++++++++++++ > t/db_dependent/api/v1/auth.t | 116 +++++++++++++++ > 3 files changed, 450 insertions(+), 236 deletions(-) > create mode 100644 Koha/REST/V1/Auth.pm > create mode 100644 t/db_dependent/api/v1/auth.t > >diff --git a/Koha/REST/V1.pm b/Koha/REST/V1.pm >index e307552..e0ac94b 100644 >--- a/Koha/REST/V1.pm >+++ b/Koha/REST/V1.pm >@@ -51,244 +51,13 @@ sub startup { > $self->secrets([$secret_passphrase]); > } > >- $self->plugin(Swagger2 => { >+ $self->plugin(OpenAPI => { > url => $self->home->rel_file("api/v1/swagger/swagger.json"), >- validate => 1, >- spec_path => '/spec' >+ route => $self->routes->under('/api/v1')->to('Auth#under'), >+ allow_invalid_ref => 1, # required by our spec because $ref directly under >+ # Paths-, Parameters-, Definitions- & Info-object >+ # is not allowed by the OpenAPI specification. > }); > } > >-=head3 authenticate_api_request >- >-Validates authentication and allows access if authorization is not required or >-if authorization is required and user has required permissions to access. >- >-This subroutine is called before every request to API. >- >-=cut >- >-sub authenticate_api_request { >- my ($next, $c, $action_spec) = @_; >- >- my ($session, $user); >- my $cookie = $c->cookie('CGISESSID'); >- # Mojo doesn't use %ENV the way CGI apps do >- # Manually pass the remote_address to check_auth_cookie >- my $remote_addr = $c->tx->remote_address; >- my ($status, $sessionID) = check_cookie_auth( >- $cookie, undef, >- { remote_addr => $remote_addr }); >- if ($status eq "ok") { >- $session = get_session($sessionID); >- $user = Koha::Patrons->find($session->param('number')); >- $c->stash('koha.user' => $user); >- } >- else { >- return $c->render_swagger( >- { error => "Authentication failure." }, >- {}, >- 401 >- ) if $cookie and $action_spec->{'x-koha-authorization'}; >- } >- >- >- # Then check the parameters >- my @query_errors = validate_query_parameters( $c, $action_spec ); >- >- # We do not need any authorization >- unless ( $action_spec->{'x-koha-authorization'} ) { >- return @query_errors >- ? $c->render_swagger({}, \@query_errors, 400) >- : $next->($c); >- } >- >- unless ($user) { >- return $c->render_swagger({ error => "Authentication required." },{},401); >- } >- >- my $authorization = $action_spec->{'x-koha-authorization'}; >- my $permissions = $authorization->{'permissions'}; >- >- # Check if the user is authorized >- if ( C4::Auth::haspermission($user->userid, $permissions) >- or allow_owner($c, $authorization, $user) >- or allow_guarantor($c, $authorization, $user) ) { >- >- # Return the query errors if exist >- return $c->render_swagger({}, \@query_errors, 400) if @query_errors; >- >- # Everything is ok >- return $next->($c) >- } >- >- return $c->render_swagger( >- { error => "Authorization failure. Missing required permission(s).", >- required_permissions => $permissions }, >- {}, >- 403 >- ); >-} >- >-sub validate_query_parameters { >- my ( $c, $action_spec ) = @_; >- >- # Check for malformed query parameters >- my @errors; >- my %valid_parameters = map { ( $_->{in} eq 'query' ) ? ( $_->{name} => 1 ) : () } @{ $action_spec->{parameters} }; >- my $existing_params = $c->req->query_params->to_hash; >- for my $param ( keys %{$existing_params} ) { >- push @errors, { path => "/query/" . $param, message => 'Malformed query string' } unless exists $valid_parameters{$param}; >- } >- return @errors; >-} >- >- >-=head3 allow_owner >- >-Allows access to object for its owner. >- >-There are endpoints that should allow access for the object owner even if they >-do not have the required permission, e.g. access an own reserve. This can be >-achieved by defining the operation as follows: >- >-"/holds/{reserve_id}": { >- "get": { >- ..., >- "x-koha-authorization": { >- "allow-owner": true, >- "permissions": { >- "borrowers": "1" >- } >- } >- } >-} >- >-=cut >- >-sub allow_owner { >- my ($c, $authorization, $user) = @_; >- >- return unless $authorization->{'allow-owner'}; >- >- return check_object_ownership($c, $user) if $user and $c; >-} >- >-=head3 allow_guarantor >- >-Same as "allow_owner", but checks if the object is owned by one of C<$user>'s >-guarantees. >- >-=cut >- >-sub allow_guarantor { >- my ($c, $authorization, $user) = @_; >- >- if (!$c || !$user || !$authorization || !$authorization->{'allow-guarantor'}){ >- return; >- } >- >- my $guarantees = $user->guarantees->as_list; >- foreach my $guarantee (@{$guarantees}) { >- return 1 if check_object_ownership($c, $guarantee); >- } >-} >- >-=head3 check_object_ownership >- >-Determines ownership of an object from request parameters. >- >-As introducing an endpoint that allows access for object's owner; if the >-parameter that will be used to determine ownership is not already inside >-$parameters, add a new subroutine that checks the ownership and extend >-$parameters to contain a key with parameter_name and a value of a subref to >-the subroutine that you created. >- >-=cut >- >-sub check_object_ownership { >- my ($c, $user) = @_; >- >- return if not $c or not $user; >- >- my $parameters = { >- accountlines_id => \&_object_ownership_by_accountlines_id, >- borrowernumber => \&_object_ownership_by_borrowernumber, >- checkout_id => \&_object_ownership_by_checkout_id, >- reserve_id => \&_object_ownership_by_reserve_id, >- }; >- >- foreach my $param ( keys %{ $parameters } ) { >- my $check_ownership = $parameters->{$param}; >- if ($c->stash($param)) { >- return &$check_ownership($c, $user, $c->stash($param)); >- } >- elsif ($c->param($param)) { >- return &$check_ownership($c, $user, $c->param($param)); >- } >- elsif ($c->req->json && $c->req->json->{$param}) { >- return 1 if &$check_ownership($c, $user, $c->req->json->{$param}); >- } >- } >-} >- >-=head3 _object_ownership_by_accountlines_id >- >-Finds a Koha::Account::Line-object by C<$accountlines_id> and checks if it >-belongs to C<$user>. >- >-=cut >- >-sub _object_ownership_by_accountlines_id { >- my ($c, $user, $accountlines_id) = @_; >- >- my $accountline = Koha::Account::Lines->find($accountlines_id); >- return $accountline && $user->borrowernumber == $accountline->borrowernumber; >-} >- >-=head3 _object_ownership_by_borrowernumber >- >-Compares C<$borrowernumber> to currently logged in C<$user>. >- >-=cut >- >-sub _object_ownership_by_borrowernumber { >- my ($c, $user, $borrowernumber) = @_; >- >- return $user->borrowernumber == $borrowernumber; >-} >- >-=head3 _object_ownership_by_checkout_id >- >-First, attempts to find a Koha::Checkout-object by C<$issue_id>. If we find one, >-compare its borrowernumber to currently logged in C<$user>. However, if an issue >-is not found, attempt to find a Koha::Old::Checkout-object instead and compare its >-borrowernumber to currently logged in C<$user>. >- >-=cut >- >-sub _object_ownership_by_checkout_id { >- my ($c, $user, $issue_id) = @_; >- >- my $issue = Koha::Checkouts->find($issue_id); >- $issue = Koha::Old::Checkouts->find($issue_id) unless $issue; >- return $issue && $issue->borrowernumber >- && $user->borrowernumber == $issue->borrowernumber; >-} >- >-=head3 _object_ownership_by_reserve_id >- >-Finds a Koha::Hold-object by C<$reserve_id> and checks if it >-belongs to C<$user>. >- >-TODO: Also compare against old_reserves >- >-=cut >- >-sub _object_ownership_by_reserve_id { >- my ($c, $user, $reserve_id) = @_; >- >- my $reserve = Koha::Holds->find($reserve_id); >- return $reserve && $user->borrowernumber == $reserve->borrowernumber; >-} >- > 1; >diff --git a/Koha/REST/V1/Auth.pm b/Koha/REST/V1/Auth.pm >new file mode 100644 >index 0000000..aa6dec9 >--- /dev/null >+++ b/Koha/REST/V1/Auth.pm >@@ -0,0 +1,329 @@ >+package Koha::REST::V1::Auth; >+ >+# Copyright Koha-Suomi Oy 2017 >+# >+# 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, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use C4::Auth qw( check_cookie_auth get_session haspermission ); >+ >+use Koha::Patrons; >+ >+use Koha::Exceptions; >+use Koha::Exceptions::Authentication; >+use Koha::Exceptions::Authorization; >+ >+use Scalar::Util qw( blessed ); >+use Try::Tiny; >+ >+=head3 under >+ >+This subroutine is called before every request to API. >+ >+=cut >+ >+sub under { >+ my $c = shift->openapi->valid_input or return;; >+ >+ my $status = 0; >+ try { >+ >+ $status = authenticate_api_request($c); >+ >+ } catch { >+ unless (blessed($_)) { >+ return $c->render( >+ status => 500, >+ json => { error => 'Something went wrong, check the logs.' } >+ ); >+ } >+ if ($_->isa('Koha::Exceptions::UnderMaintenance')) { >+ return $c->render(status => 503, json => { error => $_->error }); >+ } >+ elsif ($_->isa('Koha::Exceptions::Authentication::SessionExpired')) { >+ return $c->render(status => 401, json => { error => $_->error }); >+ } >+ elsif ($_->isa('Koha::Exceptions::Authentication::Required')) { >+ return $c->render(status => 401, json => { error => $_->error }); >+ } >+ elsif ($_->isa('Koha::Exceptions::Authentication')) { >+ return $c->render(status => 500, json => { error => $_->error }); >+ } >+ elsif ($_->isa('Koha::Exceptions::BadParameter')) { >+ return $c->render(status => 400, json => $_->error ); >+ } >+ elsif ($_->isa('Koha::Exceptions::Authorization::Unauthorized')) { >+ return $c->render(status => 403, json => { >+ error => $_->error, >+ required_permissions => $_->required_permissions, >+ }); >+ } >+ elsif ($_->isa('Koha::Exceptions')) { >+ return $c->render(status => 500, json => { error => $_->error }); >+ } >+ else { >+ return $c->render( >+ status => 500, >+ json => { error => 'Something went wrong, check the logs.' } >+ ); >+ } >+ }; >+ >+ return $status; >+} >+ >+=head3 authenticate_api_request >+ >+Validates authentication and allows access if authorization is not required or >+if authorization is required and user has required permissions to access. >+ >+=cut >+ >+sub authenticate_api_request { >+ my ( $c ) = @_; >+ >+ my $spec = $c->match->endpoint->pattern->defaults->{'openapi.op_spec'}; >+ my $authorization = $spec->{'x-koha-authorization'}; >+ my $cookie = $c->cookie('CGISESSID'); >+ my ($session, $user); >+ # Mojo doesn't use %ENV the way CGI apps do >+ # Manually pass the remote_address to check_auth_cookie >+ my $remote_addr = $c->tx->remote_address; >+ my ($status, $sessionID) = check_cookie_auth( >+ $cookie, undef, >+ { remote_addr => $remote_addr }); >+ if ($status eq "ok") { >+ $session = get_session($sessionID); >+ $user = Koha::Patrons->find($session->param('number')); >+ $c->stash('koha.user' => $user); >+ } >+ elsif ($status eq "maintenance") { >+ Koha::Exceptions::UnderMaintenance->throw( >+ error => 'System is under maintenance.' >+ ); >+ } >+ elsif ($status eq "expired" and $authorization) { >+ Koha::Exceptions::Authentication::SessionExpired->throw( >+ error => 'Session has been expired.' >+ ); >+ } >+ elsif ($status eq "failed" and $authorization) { >+ Koha::Exceptions::Authentication::Required->throw( >+ error => 'Authentication failure.' >+ ); >+ } >+ elsif ($authorization) { >+ Koha::Exceptions::Authentication->throw( >+ error => 'Unexpected authentication status.' >+ ); >+ } >+ >+ # We do not need any authorization >+ unless ($authorization) { >+ # Check the parameters >+ validate_query_parameters( $c, $spec ); >+ return 1; >+ } >+ >+ my $permissions = $authorization->{'permissions'}; >+ # Check if the user is authorized >+ if ( haspermission($user->userid, $permissions) >+ or allow_owner($c, $authorization, $user) >+ or allow_guarantor($c, $authorization, $user) ) { >+ >+ validate_query_parameters( $c, $spec ); >+ >+ # Everything is ok >+ return 1; >+ } >+ >+ Koha::Exceptions::Authorization::Unauthorized->throw( >+ error => "Authorization failure. Missing required permission(s).", >+ required_permissions => $permissions, >+ ); >+} >+sub validate_query_parameters { >+ my ( $c, $action_spec ) = @_; >+ >+ # Check for malformed query parameters >+ my @errors; >+ my %valid_parameters = map { ( $_->{in} eq 'query' ) ? ( $_->{name} => 1 ) : () } @{ $action_spec->{parameters} }; >+ my $existing_params = $c->req->query_params->to_hash; >+ for my $param ( keys %{$existing_params} ) { >+ push @errors, { path => "/query/" . $param, message => 'Malformed query string' } unless exists $valid_parameters{$param}; >+ } >+ >+ Koha::Exceptions::BadParameter->throw( >+ error => \@errors >+ ) if @errors; >+} >+ >+ >+=head3 allow_owner >+ >+Allows access to object for its owner. >+ >+There are endpoints that should allow access for the object owner even if they >+do not have the required permission, e.g. access an own reserve. This can be >+achieved by defining the operation as follows: >+ >+"/holds/{reserve_id}": { >+ "get": { >+ ..., >+ "x-koha-authorization": { >+ "allow-owner": true, >+ "permissions": { >+ "borrowers": "1" >+ } >+ } >+ } >+} >+ >+=cut >+ >+sub allow_owner { >+ my ($c, $authorization, $user) = @_; >+ >+ return unless $authorization->{'allow-owner'}; >+ >+ return check_object_ownership($c, $user) if $user and $c; >+} >+ >+=head3 allow_guarantor >+ >+Same as "allow_owner", but checks if the object is owned by one of C<$user>'s >+guarantees. >+ >+=cut >+ >+sub allow_guarantor { >+ my ($c, $authorization, $user) = @_; >+ >+ if (!$c || !$user || !$authorization || !$authorization->{'allow-guarantor'}){ >+ return; >+ } >+ >+ my $guarantees = $user->guarantees->as_list; >+ foreach my $guarantee (@{$guarantees}) { >+ return 1 if check_object_ownership($c, $guarantee); >+ } >+} >+ >+=head3 check_object_ownership >+ >+Determines ownership of an object from request parameters. >+ >+As introducing an endpoint that allows access for object's owner; if the >+parameter that will be used to determine ownership is not already inside >+$parameters, add a new subroutine that checks the ownership and extend >+$parameters to contain a key with parameter_name and a value of a subref to >+the subroutine that you created. >+ >+=cut >+ >+sub check_object_ownership { >+ my ($c, $user) = @_; >+ >+ return if not $c or not $user; >+ >+ my $parameters = { >+ accountlines_id => \&_object_ownership_by_accountlines_id, >+ borrowernumber => \&_object_ownership_by_borrowernumber, >+ checkout_id => \&_object_ownership_by_checkout_id, >+ reserve_id => \&_object_ownership_by_reserve_id, >+ }; >+ >+ foreach my $param ( keys %{ $parameters } ) { >+ my $check_ownership = $parameters->{$param}; >+ if ($c->stash($param)) { >+ return &$check_ownership($c, $user, $c->stash($param)); >+ } >+ elsif ($c->param($param)) { >+ return &$check_ownership($c, $user, $c->param($param)); >+ } >+ elsif ($c->match->stack->[-1]->{$param}) { >+ return &$check_ownership($c, $user, $c->match->stack->[-1]->{$param}); >+ } >+ elsif ($c->req->json && $c->req->json->{$param}) { >+ return 1 if &$check_ownership($c, $user, $c->req->json->{$param}); >+ } >+ } >+} >+ >+=head3 _object_ownership_by_accountlines_id >+ >+Finds a Koha::Account::Line-object by C<$accountlines_id> and checks if it >+belongs to C<$user>. >+ >+=cut >+ >+sub _object_ownership_by_accountlines_id { >+ my ($c, $user, $accountlines_id) = @_; >+ >+ my $accountline = Koha::Account::Lines->find($accountlines_id); >+ return $accountline && $user->borrowernumber == $accountline->borrowernumber; >+} >+ >+=head3 _object_ownership_by_borrowernumber >+ >+Compares C<$borrowernumber> to currently logged in C<$user>. >+ >+=cut >+ >+sub _object_ownership_by_borrowernumber { >+ my ($c, $user, $borrowernumber) = @_; >+ >+ return $user->borrowernumber == $borrowernumber; >+} >+ >+=head3 _object_ownership_by_checkout_id >+ >+First, attempts to find a Koha::Checkout-object by C<$issue_id>. If we find one, >+compare its borrowernumber to currently logged in C<$user>. However, if an issue >+is not found, attempt to find a Koha::Old::Checkout-object instead and compare its >+borrowernumber to currently logged in C<$user>. >+ >+=cut >+ >+sub _object_ownership_by_checkout_id { >+ my ($c, $user, $issue_id) = @_; >+ >+ my $issue = Koha::Checkouts->find($issue_id); >+ $issue = Koha::Old::Checkouts->find($issue_id) unless $issue; >+ return $issue && $issue->borrowernumber >+ && $user->borrowernumber == $issue->borrowernumber; >+} >+ >+=head3 _object_ownership_by_reserve_id >+ >+Finds a Koha::Hold-object by C<$reserve_id> and checks if it >+belongs to C<$user>. >+ >+TODO: Also compare against old_reserves >+ >+=cut >+ >+sub _object_ownership_by_reserve_id { >+ my ($c, $user, $reserve_id) = @_; >+ >+ my $reserve = Koha::Holds->find($reserve_id); >+ return $reserve && $user->borrowernumber == $reserve->borrowernumber; >+} >+ >+1; >diff --git a/t/db_dependent/api/v1/auth.t b/t/db_dependent/api/v1/auth.t >new file mode 100644 >index 0000000..0e324a8 >--- /dev/null >+++ b/t/db_dependent/api/v1/auth.t >@@ -0,0 +1,116 @@ >+#!/usr/bin/env perl >+ >+# 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, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 1; >+use Test::Mojo; >+use Test::Warn; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use C4::Auth; >+use Koha::Database; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling >+# this affects the other REST api tests >+t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); >+ >+my $remote_address = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+my $tx; >+ >+subtest 'under() tests' => sub { >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ my ($borrowernumber, $session_id) = create_user_and_session(); >+ >+ # 401 (no authentication) >+ $tx = $t->ua->build_tx( GET => "/api/v1/patrons" ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(401) >+ ->json_is('/error', 'Authentication failure.'); >+ >+ # 403 (no permission) >+ $tx = $t->ua->build_tx( GET => "/api/v1/patrons" ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(403) >+ ->json_is('/error', 'Authorization failure. Missing required permission(s).'); >+ >+ # 401 (session expired) >+ my $session = C4::Auth::get_session($session_id); >+ $session->delete; >+ $session->flush; >+ $tx = $t->ua->build_tx( GET => "/api/v1/patrons" ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(401) >+ ->json_is('/error', 'Session has been expired.'); >+ >+ # 503 (under maintenance & pending update) >+ t::lib::Mocks::mock_preference('Version', 1); >+ $tx = $t->ua->build_tx( GET => "/api/v1/patrons" ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(503) >+ ->json_is('/error', 'System is under maintenance.'); >+ >+ # 503 (under maintenance & database not installed) >+ t::lib::Mocks::mock_preference('Version', undef); >+ $tx = $t->ua->build_tx( GET => "/api/v1/patrons" ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(503) >+ ->json_is('/error', 'System is under maintenance.'); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+sub create_user_and_session { >+ my $user = $builder->build( >+ { >+ source => 'Borrower', >+ value => { >+ flags => 0 >+ } >+ } >+ ); >+ >+ # Create a session for the authorized user >+ my $session = C4::Auth::get_session(''); >+ $session->param( 'number', $user->{borrowernumber} ); >+ $session->param( 'id', $user->{userid} ); >+ $session->param( 'ip', '127.0.0.1' ); >+ $session->param( 'lasttime', time() ); >+ $session->flush; >+ >+ return ( $user->{borrowernumber}, $session->id ); >+} >+ >+1; >-- >2.7.4
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 18137
:
60490
|
60491
|
60492
|
60493
|
60494
|
60495
|
60533
|
60837
|
60838
|
60839
|
60840
|
60841
|
60842
|
60843
|
60866
|
60867
|
61163
|
61166
|
62780
|
62781
|
62782
|
62783
|
62784
|
62785
|
62786
|
62787
|
62788
|
62789
|
62816
|
63025
|
63026
|
63027
|
63028
|
63029
|
63030
|
63031
|
63032
|
63033
|
63034
|
63177
|
63178
|
63179
|
63180
|
63181
|
63182
|
63183
|
63184
|
63185
|
63186
|
63187
|
63188
|
63189
|
63190
|
63191
|
63192
|
63193
|
63194
|
63195
|
63196
|
63283
|
65106
|
65107
|
65108
|
65109
|
65110
|
65111
|
65112
|
65113
|
65114
|
65115
|
65404
|
65713
|
65714
|
66802
|
66803
|
66804
|
66805
|
66806
|
66807
|
66808
|
66809
|
66810
|
66811
|
66812
|
66813
|
66814
|
66815
|
66816
|
66817
|
66818
|
66819