From c1dd51326f323721b3dc4ea2a1b7cd708d530883 Mon Sep 17 00:00:00 2001 From: Lari Taskula Date: Thu, 4 Aug 2016 13:33:14 +0300 Subject: [PATCH] Bug 17004: Add API route to authenticate patron (CGISESSID) including logout POST /auth/session (login) DELETE /auth/session (logout) Required POST data: - "password" - either "userid" or "cardnumber". To test: 1. Make sure you are logged out from Koha. 2. Make a POST request to http://yourlibrary/api/v1/auth/session with form data "userid" => and "password" => . 3. If your userid and password is correct, you should be returned with most basic patron data and your CGISESSID. 4. Also attempt with invalid login to get an error. 5. Send a DELETE request to /auth/session 6. Observe that you are no longer logged-in in Koha. 7. Run tests at b/t/db_dependent/api/v1/auth.t You may find this cURL useful: curl -X DELETE http://lib/api/v1/auth/session --cookie 'CGISESSID=88e735aaf7c6cf194a775425cbd00570' (replace CGISESSID=... with your CGISESSID) ---- I've rebased both attachments to master (restructuralized swagger definitions). And added proposed permissions as proposed by Benjamin Rokseth. Btw, very nice work Lari! Signed-off-by: Benjamin Rokseth --- Koha/REST/V1/Auth.pm | 101 +++++++++++++++++++++ api/v1/swagger/definitions.json | 3 + api/v1/swagger/definitions/session.json | 32 +++++++ api/v1/swagger/definitions/sessionid.json | 10 +++ api/v1/swagger/parameters.json | 12 +++ api/v1/swagger/parameters/auth.json | 31 +++++++ api/v1/swagger/paths.json | 3 + api/v1/swagger/paths/auth.json | 58 +++++++++++++ api/v1/swagger/swagger.min.json | 2 +- t/db_dependent/api/v1/auth.t | 140 ++++++++++++++++++++++++++++++ 10 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 Koha/REST/V1/Auth.pm create mode 100644 api/v1/swagger/definitions/session.json create mode 100644 api/v1/swagger/definitions/sessionid.json create mode 100644 api/v1/swagger/parameters/auth.json create mode 100644 api/v1/swagger/paths/auth.json create mode 100644 t/db_dependent/api/v1/auth.t diff --git a/Koha/REST/V1/Auth.pm b/Koha/REST/V1/Auth.pm new file mode 100644 index 0000000..58c8b80 --- /dev/null +++ b/Koha/REST/V1/Auth.pm @@ -0,0 +1,101 @@ +package Koha::REST::V1::Auth; + +# Copyright 2016 Koha-Suomi +# +# 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 Koha::Patrons; +use C4::Auth; + +use CGI; + +sub login { + my ($c, $args, $cb) = @_; + + my $userid = $args->{userid} || $args->{cardnumber}; + my $password = $args->{password}; + my $patron; + + return $c->$cb({ error => "Either userid or cardnumber is required " + ."- neither given." }, 403) unless ($userid); + + my $cgi = CGI->new; + $cgi->param(userid => $userid); + $cgi->param(password => $password); + my ($status, $cookie, $sessionid) = C4::Auth::check_api_auth($cgi); + + return $c->$cb({ error => "Login failed." }, 401) if $status eq "failed"; + return $c->$cb({ error => "Session expired." }, 401) if $status eq "expired"; + return $c->$cb({ error => "Database is under maintenance." }, 401) if $status eq "maintenance"; + return $c->$cb({ error => "Login failed." }, 401) unless $status eq "ok"; + + $patron = Koha::Patrons->find({ userid => $userid }) unless $patron; + $patron = Koha::Patrons->find({ cardnumber => $userid }) unless $patron; + + my $session = _swaggerize_session($sessionid, $patron); + + $c->cookie(CGISESSID => $sessionid, { path => "/" }); + + return $c->$cb($session, 201); +} + +sub logout { + my ($c, $args, $cb) = @_; + + my $sessionid = $args->{session}->{sessionid}; + $sessionid = $c->cookie('CGISESSID') unless $sessionid; + + my ($status, $sid) = C4::Auth::check_cookie_auth($sessionid); + unless ($status eq "ok") { + return $c->$cb({ error => "Invalid session id."}, 401); + } + + $c->cookie(CGISESSID => $sessionid, { path => "/", expires => 1 }); + + my $session = C4::Auth::get_session($sessionid); + $session->delete; + $session->flush; + return $c->$cb({}, 200); +} + +sub _swaggerize_session { + my ($sessionid, $patron) = @_; + + return unless ref($patron) eq 'Koha::Patron'; + + my $rawPermissions = C4::Auth::haspermission($patron->userid); # defaults to all permissions + my @permissions; + + # delete all empty permissions + while ( my ($key, $val) = each %{$rawPermissions} ) { + push @permissions, $key if $val; + } + + return { + borrowernumber => $patron->borrowernumber, + firstname => $patron->firstname, + surname => $patron->surname, + email => $patron->email, + sessionid => $sessionid, + permissions => \@permissions, + }; +} + +1; diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json index e4d7427..a17c793 100644 --- a/api/v1/swagger/definitions.json +++ b/api/v1/swagger/definitions.json @@ -1,4 +1,7 @@ { + "session": { + "$ref": "definitions/session.json" + }, "patron": { "$ref": "definitions/patron.json" }, diff --git a/api/v1/swagger/definitions/session.json b/api/v1/swagger/definitions/session.json new file mode 100644 index 0000000..253dc23 --- /dev/null +++ b/api/v1/swagger/definitions/session.json @@ -0,0 +1,32 @@ +{ + "type": "object", + "properties": { + "borrowernumber": { + "type": "string", + "description": "internally assigned user identifier" + }, + "email": { + "type": ["string", "null"], + "description": "primary email address for patron's primary address" + }, + "surname": { + "type": "string", + "description": "patron's last name" + }, + "firstname": { + "type": ["string", "null"], + "description": "patron's first name" + }, + "sessionid": { + "description": "Koha Session identifier", + "type": "string" + }, + "permissions": { + "description": "patron's permissions", + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/api/v1/swagger/definitions/sessionid.json b/api/v1/swagger/definitions/sessionid.json new file mode 100644 index 0000000..6e013aa --- /dev/null +++ b/api/v1/swagger/definitions/sessionid.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "properties": { + "sessionid": { + "description": "The Koha sessionid", + "type": "string" + } + }, + "required": ["sessionid"] +} diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json index b20e19f..87380a8 100644 --- a/api/v1/swagger/parameters.json +++ b/api/v1/swagger/parameters.json @@ -7,5 +7,17 @@ }, "holdIdPathParam": { "$ref": "parameters/hold.json#/holdIdPathParam" + }, + "cardnumberPostParam": { + "$ref": "parameters/auth.json#/cardnumberPostParam" + }, + "passwordPostParam": { + "$ref": "parameters/auth.json#/passwordPostParam" + }, + "useridPostParam": { + "$ref": "parameters/auth.json#/useridPostParam" + }, + "sessionidBodyParam": { + "$ref": "parameters/auth.json#/sessionidBodyParam" } } diff --git a/api/v1/swagger/parameters/auth.json b/api/v1/swagger/parameters/auth.json new file mode 100644 index 0000000..05fdbc1 --- /dev/null +++ b/api/v1/swagger/parameters/auth.json @@ -0,0 +1,31 @@ +{ + "cardnumberPostParam": { + "name": "cardnumber", + "in": "formData", + "description": "Borrower's card's barcode/identifier", + "required": false, + "type": "string" + }, + "passwordPostParam": { + "name": "password", + "in": "formData", + "required": true, + "type": "string" + }, + "useridPostParam": { + "name": "userid", + "in": "formData", + "description": "The userid of the Borrower, unique value", + "required": false, + "type": "string" + }, + "sessionidBodyParam": { + "name": "session", + "description": "The CGISESSID Cookie used to authenticate a session", + "in": "body", + "required": false, + "schema" : { + "$ref": "../definitions/sessionid.json" + } + } +} diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json index f00b1b4..2e8fd38 100644 --- a/api/v1/swagger/paths.json +++ b/api/v1/swagger/paths.json @@ -1,4 +1,7 @@ { + "/auth/session": { + "$ref": "paths/auth.json#/~1auth~1session" + }, "/holds": { "$ref": "paths/holds.json#/~1holds" }, diff --git a/api/v1/swagger/paths/auth.json b/api/v1/swagger/paths/auth.json new file mode 100644 index 0000000..bf190a9 --- /dev/null +++ b/api/v1/swagger/paths/auth.json @@ -0,0 +1,58 @@ +{ + "/auth/session": { + "post": { + "operationId": "loginAuth", + "tags": ["auth"], + "summary": "Login to Koha and get a session cookie", + "description": "Makes a 'normal' username + password login to Koha, and returns the sessionid you need put to the CGISESSID-cookie. Koha uses this cookie to track a session.\nBe aware that the authenticated session most probably is IP-locked so authenticating from one IP and passing the session to another wont work.", + "parameters": [ + { "$ref": "../parameters.json#/cardnumberPostParam" }, + { "$ref": "../parameters.json#/useridPostParam" }, + { "$ref": "../parameters.json#/passwordPostParam" } + ], + "produces": [ + "application/json" + ], + "responses": { + "201": { + "description": "A borrower with SSO-relevant fields", + "schema": { + "$ref": "../definitions.json#/session" + } + }, + "401": { + "description": "Bad username/cardnumber and/or password", + "schema": { + "$ref": "../definitions.json#/error" + } + } + } + }, + "delete": { + "operationId": "logoutAuth", + "tags": ["auth"], + "summary": "Logout from Koha.", + "description": "Logouts user from Koha by marking session as expired. sessionid is optional, if not given, logs out currently logged in user", + "parameters": [ + { "$ref": "../parameters.json#/sessionidBodyParam" } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Successfully logged out", + "schema": { + "type": "object" + } + }, + "401": { + "description": "Bad session id", + "schema": { + "$ref": "../definitions.json#/error" + } + } + } + } + } +} diff --git a/api/v1/swagger/swagger.min.json b/api/v1/swagger/swagger.min.json index e09057f..e4b246b 100644 --- a/api/v1/swagger/swagger.min.json +++ b/api/v1/swagger/swagger.min.json @@ -1 +1 @@ -{"definitions":{"error":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"},"hold":{"properties":{"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"borrowernumber":{"type":"string","description":"internally assigned user identifier"},"expirationdate":{"description":"the date the hold expires"},"reservedate":{"description":"the date the hold was placed"},"priority":{"description":"where in the queue the patron sits"},"biblionumber":{"type":"string","description":"internally assigned biblio identifier"},"branchcode":{"type":["string","null"],"description":"internally assigned branch identifier"},"reservenotes":{"description":"notes related to this hold"},"reminderdate":{"description":"currently unused"},"lowestPriority":{"description":""},"timestamp":{"description":"date and time the hold was last updated"},"cancellationdate":{"description":"the date the hold was cancelled"},"suspend":{"description":""},"itemnumber":{"type":["string","null"],"description":"internally assigned item identifier"},"suspend_until":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"notificationdate":{"description":"currently unused"}},"type":"object"},"holds":{"type":"array","items":{"type":"object","properties":{"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"borrowernumber":{"type":"string","description":"internally assigned user identifier"},"expirationdate":{"description":"the date the hold expires"},"reservedate":{"description":"the date the hold was placed"},"priority":{"description":"where in the queue the patron sits"},"biblionumber":{"type":"string","description":"internally assigned biblio identifier"},"branchcode":{"type":["string","null"],"description":"internally assigned branch identifier"},"reservenotes":{"description":"notes related to this hold"},"reminderdate":{"description":"currently unused"},"lowestPriority":{"description":""},"timestamp":{"description":"date and time the hold was last updated"},"cancellationdate":{"description":"the date the hold was cancelled"},"suspend":{"description":""},"itemnumber":{"type":["string","null"],"description":"internally assigned item identifier"},"suspend_until":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"notificationdate":{"description":"currently unused"}}}},"patron":{"properties":{"gonenoaddress":{"type":["string","null"],"description":"set to 1 if library marked this patron as having an unconfirmed address"},"address":{"type":"string","description":"first address line of patron's primary address"},"streetnumber":{"description":"street number of patron's primary address","type":["string","null"]},"sort1":{"type":["string","null"],"description":"a field that can be used for any information unique to the library"},"lost":{"description":"set to 1 if library marked this patron as having lost his card","type":["string","null"]},"altcontactcountry":{"type":["string","null"],"description":"the country for the alternate contact for the patron"},"B_state":{"type":["string","null"],"description":"state or province of patron's alternate address"},"altcontactstate":{"description":"the state for the alternate contact for the patron","type":["string","null"]},"mobile":{"type":["string","null"],"description":"the other phone number for patron's primary address"},"B_address2":{"type":["string","null"],"description":"second address line of patron's alternate address"},"B_zipcode":{"description":"zip or postal code of patron's alternate address","type":["string","null"]},"fax":{"type":["string","null"],"description":"fax number for patron's primary address"},"city":{"type":"string","description":"city or town of patron's primary address"},"sex":{"type":["string","null"],"description":"patron's gender"},"categorycode":{"type":"string","description":"code of patron's category"},"relationship":{"description":"used for children to include the relationship to their guarantor","type":["string","null"]},"sort2":{"description":"a field that can be used for any information unique to the library","type":["string","null"]},"email":{"description":"primary email address for patron's primary address","type":["string","null"]},"title":{"type":["string","null"],"description":"patron's title"},"streettype":{"type":["string","null"],"description":"street type of patron's primary address"},"B_phone":{"type":["string","null"],"description":"phone number for patron's alternate address"},"contactnote":{"description":"a note related to patron's alternate address","type":["string","null"]},"password":{"description":"patron's encrypted password","type":["string","null"]},"altcontactphone":{"type":["string","null"],"description":"the phone number for the alternate contact for the patron"},"sms_provider_id":{"description":"the provider of the mobile phone number defined in smsalertnumber","type":["string","null"]},"B_country":{"type":["string","null"],"description":"country of patron's alternate address"},"dateexpiry":{"type":["string","null"],"description":"date the patron's card is set to expire"},"updated_on":{"type":"string","description":"time of last change could be useful for synchronization with external systems (among others)"},"contactfirstname":{"type":["string","null"],"description":"used for children to include first name of guarantor"},"surname":{"description":"patron's last name","type":"string"},"othernames":{"type":["string","null"],"description":"any other names associated with the patron"},"country":{"type":["string","null"],"description":"country of patron's primary address"},"B_streetnumber":{"type":["string","null"],"description":"street number of patron's alternate address"},"phonepro":{"description":"secondary phone number for patron's primary address","type":["string","null"]},"borrowernumber":{"type":"string","description":"internally assigned user identifier"},"guarantorid":{"type":["string","null"],"description":"borrowernumber used for children or professionals to link them to guarantor or organizations"},"altcontactzipcode":{"description":"the zipcode for the alternate contact for the patron","type":["string","null"]},"altcontactsurname":{"description":"surname or last name of the alternate contact for the patron","type":["string","null"]},"zipcode":{"type":["string","null"],"description":"zip or postal code of patron's primary address"},"contactname":{"type":["string","null"],"description":"used for children and professionals to include surname or last name of guarantor or organization name"},"emailpro":{"description":"secondary email address for patron's primary address","type":["string","null"]},"borrowernotes":{"type":["string","null"],"description":"a note on the patron's account"},"B_streettype":{"type":["string","null"],"description":"street type of patron's alternate address"},"smsalertnumber":{"description":"the mobile phone number where the patron would like to receive notices (if SMS turned on)","type":["string","null"]},"state":{"type":["string","null"],"description":"state or province of patron's primary address"},"altcontactfirstname":{"type":["string","null"],"description":"first name of alternate contact for the patron"},"address2":{"type":["string","null"],"description":"second address line of patron's primary address"},"debarredcomment":{"type":["string","null"],"description":"comment on the stop of the patron"},"B_email":{"type":["string","null"],"description":"email address for patron's alternate address"},"B_address":{"description":"first address line of patron's alternate address","type":["string","null"]},"branchcode":{"type":"string","description":"code of patron's home branch"},"userid":{"type":["string","null"],"description":"patron's login"},"altcontactaddress1":{"description":"the first address line for the alternate contact for the patron","type":["string","null"]},"debarred":{"type":["string","null"],"description":"until this date the patron can only check-in"},"privacy_guarantor_checkouts":{"type":"string","description":"controls if relatives can see this patron's checkouts"},"contacttitle":{"type":["string","null"],"description":"used for children to include title of guarantor"},"privacy":{"description":"patron's privacy settings related to their reading history","type":"string"},"flags":{"description":"a number associated with the patron's permissions","type":["string","null"]},"dateofbirth":{"description":"patron's date of birth","type":["string","null"]},"checkprevcheckout":{"description":"produce a warning for this patron if this item has previously been checked out to this patron if 'yes', not if 'no', defer to category setting if 'inherit'","type":"string"},"initials":{"type":["string","null"],"description":"initials of the patron"},"B_city":{"type":["string","null"],"description":"city or town of patron's alternate address"},"cardnumber":{"type":["string","null"],"description":"library assigned user identifier"},"altcontactaddress3":{"description":"the city for the alternate contact for the patron","type":["string","null"]},"phone":{"description":"primary phone number for patron's primary address","type":["string","null"]},"altcontactaddress2":{"type":["string","null"],"description":"the second address line for the alternate contact for the patron"},"firstname":{"type":["string","null"],"description":"patron's first name"},"dateenrolled":{"description":"date the patron was added to Koha","type":["string","null"]},"opacnote":{"description":"a note on the patron's account visible in OPAC and staff client","type":["string","null"]}},"type":"object"}},"paths":{"\/patrons\/{borrowernumber}":{"get":{"operationId":"getPatron","tags":["patrons"],"produces":["application\/json"],"parameters":[{"description":"Internal patron identifier","type":"integer","required":true,"in":"path","name":"borrowernumber"}],"responses":{"404":{"description":"Patron not found","schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}}},"200":{"schema":{"type":"object","properties":{"gonenoaddress":{"type":["string","null"],"description":"set to 1 if library marked this patron as having an unconfirmed address"},"address":{"type":"string","description":"first address line of patron's primary address"},"streetnumber":{"description":"street number of patron's primary address","type":["string","null"]},"sort1":{"type":["string","null"],"description":"a field that can be used for any information unique to the library"},"lost":{"description":"set to 1 if library marked this patron as having lost his card","type":["string","null"]},"altcontactcountry":{"type":["string","null"],"description":"the country for the alternate contact for the patron"},"B_state":{"type":["string","null"],"description":"state or province of patron's alternate address"},"altcontactstate":{"description":"the state for the alternate contact for the patron","type":["string","null"]},"mobile":{"type":["string","null"],"description":"the other phone number for patron's primary address"},"B_address2":{"type":["string","null"],"description":"second address line of patron's alternate address"},"B_zipcode":{"description":"zip or postal code of patron's alternate address","type":["string","null"]},"fax":{"type":["string","null"],"description":"fax number for patron's primary address"},"city":{"type":"string","description":"city or town of patron's primary address"},"sex":{"type":["string","null"],"description":"patron's gender"},"categorycode":{"type":"string","description":"code of patron's category"},"relationship":{"description":"used for children to include the relationship to their guarantor","type":["string","null"]},"sort2":{"description":"a field that can be used for any information unique to the library","type":["string","null"]},"email":{"description":"primary email address for patron's primary address","type":["string","null"]},"title":{"type":["string","null"],"description":"patron's title"},"streettype":{"type":["string","null"],"description":"street type of patron's primary address"},"B_phone":{"type":["string","null"],"description":"phone number for patron's alternate address"},"contactnote":{"description":"a note related to patron's alternate address","type":["string","null"]},"password":{"description":"patron's encrypted password","type":["string","null"]},"altcontactphone":{"type":["string","null"],"description":"the phone number for the alternate contact for the patron"},"sms_provider_id":{"description":"the provider of the mobile phone number defined in smsalertnumber","type":["string","null"]},"B_country":{"type":["string","null"],"description":"country of patron's alternate address"},"dateexpiry":{"type":["string","null"],"description":"date the patron's card is set to expire"},"updated_on":{"type":"string","description":"time of last change could be useful for synchronization with external systems (among others)"},"contactfirstname":{"type":["string","null"],"description":"used for children to include first name of guarantor"},"surname":{"description":"patron's last name","type":"string"},"othernames":{"type":["string","null"],"description":"any other names associated with the patron"},"country":{"type":["string","null"],"description":"country of patron's primary address"},"B_streetnumber":{"type":["string","null"],"description":"street number of patron's alternate address"},"phonepro":{"description":"secondary phone number for patron's primary address","type":["string","null"]},"borrowernumber":{"type":"string","description":"internally assigned user identifier"},"guarantorid":{"type":["string","null"],"description":"borrowernumber used for children or professionals to link them to guarantor or organizations"},"altcontactzipcode":{"description":"the zipcode for the alternate contact for the patron","type":["string","null"]},"altcontactsurname":{"description":"surname or last name of the alternate contact for the patron","type":["string","null"]},"zipcode":{"type":["string","null"],"description":"zip or postal code of patron's primary address"},"contactname":{"type":["string","null"],"description":"used for children and professionals to include surname or last name of guarantor or organization name"},"emailpro":{"description":"secondary email address for patron's primary address","type":["string","null"]},"borrowernotes":{"type":["string","null"],"description":"a note on the patron's account"},"B_streettype":{"type":["string","null"],"description":"street type of patron's alternate address"},"smsalertnumber":{"description":"the mobile phone number where the patron would like to receive notices (if SMS turned on)","type":["string","null"]},"state":{"type":["string","null"],"description":"state or province of patron's primary address"},"altcontactfirstname":{"type":["string","null"],"description":"first name of alternate contact for the patron"},"address2":{"type":["string","null"],"description":"second address line of patron's primary address"},"debarredcomment":{"type":["string","null"],"description":"comment on the stop of the patron"},"B_email":{"type":["string","null"],"description":"email address for patron's alternate address"},"B_address":{"description":"first address line of patron's alternate address","type":["string","null"]},"branchcode":{"type":"string","description":"code of patron's home branch"},"userid":{"type":["string","null"],"description":"patron's login"},"altcontactaddress1":{"description":"the first address line for the alternate contact for the patron","type":["string","null"]},"debarred":{"type":["string","null"],"description":"until this date the patron can only check-in"},"privacy_guarantor_checkouts":{"type":"string","description":"controls if relatives can see this patron's checkouts"},"contacttitle":{"type":["string","null"],"description":"used for children to include title of guarantor"},"privacy":{"description":"patron's privacy settings related to their reading history","type":"string"},"flags":{"description":"a number associated with the patron's permissions","type":["string","null"]},"dateofbirth":{"description":"patron's date of birth","type":["string","null"]},"checkprevcheckout":{"description":"produce a warning for this patron if this item has previously been checked out to this patron if 'yes', not if 'no', defer to category setting if 'inherit'","type":"string"},"initials":{"type":["string","null"],"description":"initials of the patron"},"B_city":{"type":["string","null"],"description":"city or town of patron's alternate address"},"cardnumber":{"type":["string","null"],"description":"library assigned user identifier"},"altcontactaddress3":{"description":"the city for the alternate contact for the patron","type":["string","null"]},"phone":{"description":"primary phone number for patron's primary address","type":["string","null"]},"altcontactaddress2":{"type":["string","null"],"description":"the second address line for the alternate contact for the patron"},"firstname":{"type":["string","null"],"description":"patron's first name"},"dateenrolled":{"description":"date the patron was added to Koha","type":["string","null"]},"opacnote":{"description":"a note on the patron's account visible in OPAC and staff client","type":["string","null"]}}},"description":"A patron"},"403":{"description":"Access forbidden","schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}}}}}},"\/patrons":{"get":{"tags":["patrons"],"produces":["application\/json"],"responses":{"200":{"schema":{"items":{"properties":{"gonenoaddress":{"type":["string","null"],"description":"set to 1 if library marked this patron as having an unconfirmed address"},"address":{"type":"string","description":"first address line of patron's primary address"},"streetnumber":{"description":"street number of patron's primary address","type":["string","null"]},"sort1":{"type":["string","null"],"description":"a field that can be used for any information unique to the library"},"lost":{"description":"set to 1 if library marked this patron as having lost his card","type":["string","null"]},"altcontactcountry":{"type":["string","null"],"description":"the country for the alternate contact for the patron"},"B_state":{"type":["string","null"],"description":"state or province of patron's alternate address"},"altcontactstate":{"description":"the state for the alternate contact for the patron","type":["string","null"]},"mobile":{"type":["string","null"],"description":"the other phone number for patron's primary address"},"B_address2":{"type":["string","null"],"description":"second address line of patron's alternate address"},"B_zipcode":{"description":"zip or postal code of patron's alternate address","type":["string","null"]},"fax":{"type":["string","null"],"description":"fax number for patron's primary address"},"city":{"type":"string","description":"city or town of patron's primary address"},"sex":{"type":["string","null"],"description":"patron's gender"},"categorycode":{"type":"string","description":"code of patron's category"},"relationship":{"description":"used for children to include the relationship to their guarantor","type":["string","null"]},"sort2":{"description":"a field that can be used for any information unique to the library","type":["string","null"]},"email":{"description":"primary email address for patron's primary address","type":["string","null"]},"title":{"type":["string","null"],"description":"patron's title"},"streettype":{"type":["string","null"],"description":"street type of patron's primary address"},"B_phone":{"type":["string","null"],"description":"phone number for patron's alternate address"},"contactnote":{"description":"a note related to patron's alternate address","type":["string","null"]},"password":{"description":"patron's encrypted password","type":["string","null"]},"altcontactphone":{"type":["string","null"],"description":"the phone number for the alternate contact for the patron"},"sms_provider_id":{"description":"the provider of the mobile phone number defined in smsalertnumber","type":["string","null"]},"B_country":{"type":["string","null"],"description":"country of patron's alternate address"},"dateexpiry":{"type":["string","null"],"description":"date the patron's card is set to expire"},"updated_on":{"type":"string","description":"time of last change could be useful for synchronization with external systems (among others)"},"contactfirstname":{"type":["string","null"],"description":"used for children to include first name of guarantor"},"surname":{"description":"patron's last name","type":"string"},"othernames":{"type":["string","null"],"description":"any other names associated with the patron"},"country":{"type":["string","null"],"description":"country of patron's primary address"},"B_streetnumber":{"type":["string","null"],"description":"street number of patron's alternate address"},"phonepro":{"description":"secondary phone number for patron's primary address","type":["string","null"]},"borrowernumber":{"type":"string","description":"internally assigned user identifier"},"guarantorid":{"type":["string","null"],"description":"borrowernumber used for children or professionals to link them to guarantor or organizations"},"altcontactzipcode":{"description":"the zipcode for the alternate contact for the patron","type":["string","null"]},"altcontactsurname":{"description":"surname or last name of the alternate contact for the patron","type":["string","null"]},"zipcode":{"type":["string","null"],"description":"zip or postal code of patron's primary address"},"contactname":{"type":["string","null"],"description":"used for children and professionals to include surname or last name of guarantor or organization name"},"emailpro":{"description":"secondary email address for patron's primary address","type":["string","null"]},"borrowernotes":{"type":["string","null"],"description":"a note on the patron's account"},"B_streettype":{"type":["string","null"],"description":"street type of patron's alternate address"},"smsalertnumber":{"description":"the mobile phone number where the patron would like to receive notices (if SMS turned on)","type":["string","null"]},"state":{"type":["string","null"],"description":"state or province of patron's primary address"},"altcontactfirstname":{"type":["string","null"],"description":"first name of alternate contact for the patron"},"address2":{"type":["string","null"],"description":"second address line of patron's primary address"},"debarredcomment":{"type":["string","null"],"description":"comment on the stop of the patron"},"B_email":{"type":["string","null"],"description":"email address for patron's alternate address"},"B_address":{"description":"first address line of patron's alternate address","type":["string","null"]},"branchcode":{"type":"string","description":"code of patron's home branch"},"userid":{"type":["string","null"],"description":"patron's login"},"altcontactaddress1":{"description":"the first address line for the alternate contact for the patron","type":["string","null"]},"debarred":{"type":["string","null"],"description":"until this date the patron can only check-in"},"privacy_guarantor_checkouts":{"type":"string","description":"controls if relatives can see this patron's checkouts"},"contacttitle":{"type":["string","null"],"description":"used for children to include title of guarantor"},"privacy":{"description":"patron's privacy settings related to their reading history","type":"string"},"flags":{"description":"a number associated with the patron's permissions","type":["string","null"]},"dateofbirth":{"description":"patron's date of birth","type":["string","null"]},"checkprevcheckout":{"description":"produce a warning for this patron if this item has previously been checked out to this patron if 'yes', not if 'no', defer to category setting if 'inherit'","type":"string"},"initials":{"type":["string","null"],"description":"initials of the patron"},"B_city":{"type":["string","null"],"description":"city or town of patron's alternate address"},"cardnumber":{"type":["string","null"],"description":"library assigned user identifier"},"altcontactaddress3":{"description":"the city for the alternate contact for the patron","type":["string","null"]},"phone":{"description":"primary phone number for patron's primary address","type":["string","null"]},"altcontactaddress2":{"type":["string","null"],"description":"the second address line for the alternate contact for the patron"},"firstname":{"type":["string","null"],"description":"patron's first name"},"dateenrolled":{"description":"date the patron was added to Koha","type":["string","null"]},"opacnote":{"description":"a note on the patron's account visible in OPAC and staff client","type":["string","null"]}},"type":"object"},"type":"array"},"description":"A list of patrons"},"403":{"description":"Access forbidden","schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"}}},"operationId":"listPatrons"}},"\/holds":{"post":{"tags":["borrowers","holds"],"produces":["application\/json"],"parameters":[{"schema":{"properties":{"biblionumber":{"type":"integer","description":"Biblio internal identifier"},"branchcode":{"type":"string","description":"Pickup location"},"itemnumber":{"type":"integer","description":"Item internal identifier"},"expirationdate":{"type":"string","description":"Hold end date","format":"date"},"borrowernumber":{"description":"Borrower internal identifier","type":"integer"}},"type":"object"},"description":"A JSON object containing informations about the new hold","name":"body","in":"body","required":true}],"responses":{"403":{"description":"Hold not allowed","schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}}},"404":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Borrower not found"},"400":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Missing or wrong parameters"},"500":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Internal error"},"201":{"description":"Created hold","schema":{"properties":{"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"borrowernumber":{"type":"string","description":"internally assigned user identifier"},"expirationdate":{"description":"the date the hold expires"},"reservedate":{"description":"the date the hold was placed"},"priority":{"description":"where in the queue the patron sits"},"biblionumber":{"type":"string","description":"internally assigned biblio identifier"},"branchcode":{"type":["string","null"],"description":"internally assigned branch identifier"},"reservenotes":{"description":"notes related to this hold"},"reminderdate":{"description":"currently unused"},"lowestPriority":{"description":""},"timestamp":{"description":"date and time the hold was last updated"},"cancellationdate":{"description":"the date the hold was cancelled"},"suspend":{"description":""},"itemnumber":{"type":["string","null"],"description":"internally assigned item identifier"},"suspend_until":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"notificationdate":{"description":"currently unused"}},"type":"object"}}},"consumes":["application\/json"],"operationId":"addHold"},"get":{"operationId":"listHolds","tags":["borrowers","holds"],"produces":["application\/json"],"parameters":[{"description":"Internal reserve identifier","type":"integer","in":"query","name":"reserve_id"},{"description":"Internal borrower identifier","type":"integer","name":"borrowernumber","in":"query"},{"type":"string","description":"Reserve date","in":"query","name":"reservedate"},{"type":"integer","description":"Internal biblio identifier","in":"query","name":"biblionumber"},{"in":"query","name":"branchcode","description":"Branch code","type":"string"},{"type":"string","description":"Notification date","in":"query","name":"notificationdate"},{"name":"reminderdate","in":"query","type":"string","description":"Reminder date"},{"in":"query","name":"cancellationdate","type":"string","description":"Cancellation date"},{"description":"Reserve notes","type":"string","in":"query","name":"reservenotes"},{"description":"Priority","type":"integer","name":"priority","in":"query"},{"description":"Found status","type":"string","name":"found","in":"query"},{"description":"Time of latest update","type":"string","in":"query","name":"timestamp"},{"type":"integer","description":"Internal item identifier","name":"itemnumber","in":"query"},{"type":"string","description":"Date the item was marked as waiting for the patron","name":"waitingdate","in":"query"},{"description":"Date the hold expires","type":"string","in":"query","name":"expirationdate"},{"description":"Lowest priority","type":"integer","in":"query","name":"lowestPriority"},{"in":"query","name":"suspend","type":"integer","description":"Suspended"},{"name":"suspend_until","in":"query","description":"Suspended until","type":"string"}],"responses":{"404":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Borrower not found"},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"borrowernumber":{"type":"string","description":"internally assigned user identifier"},"expirationdate":{"description":"the date the hold expires"},"reservedate":{"description":"the date the hold was placed"},"priority":{"description":"where in the queue the patron sits"},"biblionumber":{"type":"string","description":"internally assigned biblio identifier"},"branchcode":{"type":["string","null"],"description":"internally assigned branch identifier"},"reservenotes":{"description":"notes related to this hold"},"reminderdate":{"description":"currently unused"},"lowestPriority":{"description":""},"timestamp":{"description":"date and time the hold was last updated"},"cancellationdate":{"description":"the date the hold was cancelled"},"suspend":{"description":""},"itemnumber":{"type":["string","null"],"description":"internally assigned item identifier"},"suspend_until":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"notificationdate":{"description":"currently unused"}}}},"description":"A list of holds"}}}},"\/holds\/{reserve_id}":{"put":{"consumes":["application\/json"],"operationId":"editHold","tags":["holds"],"produces":["application\/json"],"parameters":[{"description":"Internal hold identifier","type":"integer","required":true,"in":"path","name":"reserve_id"},{"name":"body","in":"body","required":true,"schema":{"properties":{"branchcode":{"type":"string","description":"Pickup location"},"suspend_until":{"description":"Suspend until","type":"string","format":"date"},"priority":{"type":"integer","description":"Position in waiting queue","minimum":1}},"type":"object"},"description":"A JSON object containing fields to modify"}],"responses":{"404":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Hold not found"},"400":{"description":"Missing or wrong parameters","schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"}},"200":{"schema":{"type":"object","properties":{"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"borrowernumber":{"type":"string","description":"internally assigned user identifier"},"expirationdate":{"description":"the date the hold expires"},"reservedate":{"description":"the date the hold was placed"},"priority":{"description":"where in the queue the patron sits"},"biblionumber":{"type":"string","description":"internally assigned biblio identifier"},"branchcode":{"type":["string","null"],"description":"internally assigned branch identifier"},"reservenotes":{"description":"notes related to this hold"},"reminderdate":{"description":"currently unused"},"lowestPriority":{"description":""},"timestamp":{"description":"date and time the hold was last updated"},"cancellationdate":{"description":"the date the hold was cancelled"},"suspend":{"description":""},"itemnumber":{"type":["string","null"],"description":"internally assigned item identifier"},"suspend_until":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"notificationdate":{"description":"currently unused"}}},"description":"Updated hold"}}},"delete":{"parameters":[{"description":"Internal hold identifier","type":"integer","required":true,"in":"path","name":"reserve_id"}],"responses":{"404":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Hold not found"},"200":{"description":"Successful deletion","schema":{"type":"object"}}},"tags":["holds"],"produces":["application\/json"],"operationId":"deleteHold"}}},"info":{"title":"Koha REST API","license":{"name":"GPL v3","url":"http:\/\/www.gnu.org\/licenses\/gpl.txt"},"contact":{"url":"http:\/\/koha-community.org\/","name":"Koha Team"},"version":"1"},"swagger":"2.0","parameters":{"borrowernumberPathParam":{"description":"Internal patron identifier","type":"integer","required":true,"in":"path","name":"borrowernumber"},"holdIdPathParam":{"description":"Internal hold identifier","type":"integer","required":true,"in":"path","name":"reserve_id"}},"basePath":"\/api\/v1"} \ No newline at end of file +{"swagger":"2.0","definitions":{"session":{"type":"object","properties":{"firstname":{"type":["string","null"],"description":"patron's first name"},"surname":{"type":"string","description":"patron's last name"},"sessionid":{"description":"Koha Session identifier","type":"string"},"email":{"type":["string","null"],"description":"primary email address for patron's primary address"},"permissions":{"items":{"type":"string"},"type":"array","description":"patron's permissions"},"borrowernumber":{"type":"string","description":"internally assigned user identifier"}}},"patron":{"type":"object","properties":{"mobile":{"type":["string","null"],"description":"the other phone number for patron's primary address"},"altcontactaddress2":{"type":["string","null"],"description":"the second address line for the alternate contact for the patron"},"gonenoaddress":{"description":"set to 1 if library marked this patron as having an unconfirmed address","type":["string","null"]},"lost":{"type":["string","null"],"description":"set to 1 if library marked this patron as having lost his card"},"othernames":{"description":"any other names associated with the patron","type":["string","null"]},"sms_provider_id":{"type":["string","null"],"description":"the provider of the mobile phone number defined in smsalertnumber"},"email":{"type":["string","null"],"description":"primary email address for patron's primary address"},"B_phone":{"description":"phone number for patron's alternate address","type":["string","null"]},"altcontactsurname":{"description":"surname or last name of the alternate contact for the patron","type":["string","null"]},"emailpro":{"type":["string","null"],"description":"secondary email address for patron's primary address"},"contacttitle":{"description":"used for children to include title of guarantor","type":["string","null"]},"B_streettype":{"type":["string","null"],"description":"street type of patron's alternate address"},"title":{"type":["string","null"],"description":"patron's title"},"zipcode":{"description":"zip or postal code of patron's primary address","type":["string","null"]},"B_address":{"description":"first address line of patron's alternate address","type":["string","null"]},"address2":{"type":["string","null"],"description":"second address line of patron's primary address"},"borrowernotes":{"description":"a note on the patron's account","type":["string","null"]},"contactnote":{"type":["string","null"],"description":"a note related to patron's alternate address"},"flags":{"description":"a number associated with the patron's permissions","type":["string","null"]},"updated_on":{"type":"string","description":"time of last change could be useful for synchronization with external systems (among others)"},"debarredcomment":{"description":"comment on the stop of the patron","type":["string","null"]},"B_email":{"type":["string","null"],"description":"email address for patron's alternate address"},"sort2":{"description":"a field that can be used for any information unique to the library","type":["string","null"]},"altcontactstate":{"type":["string","null"],"description":"the state for the alternate contact for the patron"},"initials":{"type":["string","null"],"description":"initials of the patron"},"B_state":{"type":["string","null"],"description":"state or province of patron's alternate address"},"B_country":{"description":"country of patron's alternate address","type":["string","null"]},"sort1":{"type":["string","null"],"description":"a field that can be used for any information unique to the library"},"categorycode":{"description":"code of patron's category","type":"string"},"debarred":{"type":["string","null"],"description":"until this date the patron can only check-in"},"B_streetnumber":{"type":["string","null"],"description":"street number of patron's alternate address"},"guarantorid":{"type":["string","null"],"description":"borrowernumber used for children or professionals to link them to guarantor or organizations"},"privacy":{"type":"string","description":"patron's privacy settings related to their reading history"},"B_city":{"type":["string","null"],"description":"city or town of patron's alternate address"},"firstname":{"type":["string","null"],"description":"patron's first name"},"userid":{"type":["string","null"],"description":"patron's login"},"dateofbirth":{"type":["string","null"],"description":"patron's date of birth"},"country":{"description":"country of patron's primary address","type":["string","null"]},"smsalertnumber":{"description":"the mobile phone number where the patron would like to receive notices (if SMS turned on)","type":["string","null"]},"altcontactaddress1":{"type":["string","null"],"description":"the first address line for the alternate contact for the patron"},"B_address2":{"description":"second address line of patron's alternate address","type":["string","null"]},"opacnote":{"type":["string","null"],"description":"a note on the patron's account visible in OPAC and staff client"},"cardnumber":{"type":["string","null"],"description":"library assigned user identifier"},"fax":{"description":"fax number for patron's primary address","type":["string","null"]},"altcontactfirstname":{"description":"first name of alternate contact for the patron","type":["string","null"]},"altcontactaddress3":{"description":"the city for the alternate contact for the patron","type":["string","null"]},"checkprevcheckout":{"type":"string","description":"produce a warning for this patron if this item has previously been checked out to this patron if 'yes', not if 'no', defer to category setting if 'inherit'"},"relationship":{"type":["string","null"],"description":"used for children to include the relationship to their guarantor"},"contactname":{"description":"used for children and professionals to include surname or last name of guarantor or organization name","type":["string","null"]},"streetnumber":{"type":["string","null"],"description":"street number of patron's primary address"},"phone":{"type":["string","null"],"description":"primary phone number for patron's primary address"},"streettype":{"type":["string","null"],"description":"street type of patron's primary address"},"surname":{"type":"string","description":"patron's last name"},"branchcode":{"description":"code of patron's home branch","type":["string","null"]},"contactfirstname":{"description":"used for children to include first name of guarantor","type":["string","null"]},"city":{"type":"string","description":"city or town of patron's primary address"},"altcontactzipcode":{"type":["string","null"],"description":"the zipcode for the alternate contact for the patron"},"password":{"type":["string","null"],"description":"patron's encrypted password"},"phonepro":{"description":"secondary phone number for patron's primary address","type":["string","null"]},"B_zipcode":{"description":"zip or postal code of patron's alternate address","type":["string","null"]},"altcontactcountry":{"description":"the country for the alternate contact for the patron","type":["string","null"]},"address":{"description":"first address line of patron's primary address","type":"string"},"sex":{"description":"patron's gender","type":["string","null"]},"altcontactphone":{"description":"the phone number for the alternate contact for the patron","type":["string","null"]},"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"dateexpiry":{"description":"date the patron's card is set to expire","type":["string","null"]},"privacy_guarantor_checkouts":{"type":"string","description":"controls if relatives can see this patron's checkouts"},"dateenrolled":{"description":"date the patron was added to Koha","type":["string","null"]},"state":{"type":["string","null"],"description":"state or province of patron's primary address"}}},"holds":{"items":{"type":"object","properties":{"biblionumber":{"description":"internally assigned biblio identifier","type":"string"},"priority":{"description":"where in the queue the patron sits"},"reservedate":{"description":"the date the hold was placed"},"notificationdate":{"description":"currently unused"},"reservenotes":{"description":"notes related to this hold"},"suspend_until":{"description":""},"suspend":{"description":""},"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"cancellationdate":{"description":"the date the hold was cancelled"},"reminderdate":{"description":"currently unused"},"itemnumber":{"description":"internally assigned item identifier","type":["string","null"]},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"lowestPriority":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"expirationdate":{"description":"the date the hold expires"},"timestamp":{"description":"date and time the hold was last updated"},"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"branchcode":{"description":"code of patron's home branch","type":["string","null"]}}},"type":"array"},"error":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"},"hold":{"properties":{"biblionumber":{"description":"internally assigned biblio identifier","type":"string"},"priority":{"description":"where in the queue the patron sits"},"reservedate":{"description":"the date the hold was placed"},"notificationdate":{"description":"currently unused"},"reservenotes":{"description":"notes related to this hold"},"suspend_until":{"description":""},"suspend":{"description":""},"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"cancellationdate":{"description":"the date the hold was cancelled"},"reminderdate":{"description":"currently unused"},"itemnumber":{"description":"internally assigned item identifier","type":["string","null"]},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"lowestPriority":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"expirationdate":{"description":"the date the hold expires"},"timestamp":{"description":"date and time the hold was last updated"},"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"branchcode":{"description":"code of patron's home branch","type":["string","null"]}},"type":"object"}},"basePath":"\/api\/v1","paths":{"\/holds\/{reserve_id}":{"put":{"parameters":[{"in":"path","required":true,"description":"Internal hold identifier","type":"integer","name":"reserve_id"},{"name":"body","description":"A JSON object containing fields to modify","required":true,"schema":{"properties":{"priority":{"minimum":1,"description":"Position in waiting queue","type":"integer"},"suspend_until":{"type":"string","description":"Suspend until","format":"date"},"branchcode":{"description":"Pickup location","type":"string"}},"type":"object"},"in":"body"}],"produces":["application\/json"],"consumes":["application\/json"],"operationId":"editHold","tags":["holds"],"responses":{"400":{"description":"Missing or wrong parameters","schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"}},"404":{"description":"Hold not found","schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"}},"200":{"schema":{"properties":{"biblionumber":{"description":"internally assigned biblio identifier","type":"string"},"priority":{"description":"where in the queue the patron sits"},"reservedate":{"description":"the date the hold was placed"},"notificationdate":{"description":"currently unused"},"reservenotes":{"description":"notes related to this hold"},"suspend_until":{"description":""},"suspend":{"description":""},"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"cancellationdate":{"description":"the date the hold was cancelled"},"reminderdate":{"description":"currently unused"},"itemnumber":{"description":"internally assigned item identifier","type":["string","null"]},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"lowestPriority":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"expirationdate":{"description":"the date the hold expires"},"timestamp":{"description":"date and time the hold was last updated"},"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"branchcode":{"description":"code of patron's home branch","type":["string","null"]}},"type":"object"},"description":"Updated hold"}}},"delete":{"produces":["application\/json"],"operationId":"deleteHold","tags":["holds"],"responses":{"200":{"description":"Successful deletion","schema":{"type":"object"}},"404":{"description":"Hold not found","schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"}}},"parameters":[{"name":"reserve_id","required":true,"description":"Internal hold identifier","type":"integer","in":"path"}]}},"\/holds":{"post":{"parameters":[{"in":"body","schema":{"properties":{"borrowernumber":{"description":"Borrower internal identifier","type":"integer"},"biblionumber":{"description":"Biblio internal identifier","type":"integer"},"itemnumber":{"type":"integer","description":"Item internal identifier"},"expirationdate":{"format":"date","description":"Hold end date","type":"string"},"branchcode":{"description":"Pickup location","type":"string"}},"type":"object"},"required":true,"description":"A JSON object containing informations about the new hold","name":"body"}],"consumes":["application\/json"],"tags":["borrowers","holds"],"operationId":"addHold","responses":{"500":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Internal error"},"403":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Hold not allowed"},"201":{"schema":{"properties":{"biblionumber":{"description":"internally assigned biblio identifier","type":"string"},"priority":{"description":"where in the queue the patron sits"},"reservedate":{"description":"the date the hold was placed"},"notificationdate":{"description":"currently unused"},"reservenotes":{"description":"notes related to this hold"},"suspend_until":{"description":""},"suspend":{"description":""},"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"cancellationdate":{"description":"the date the hold was cancelled"},"reminderdate":{"description":"currently unused"},"itemnumber":{"description":"internally assigned item identifier","type":["string","null"]},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"lowestPriority":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"expirationdate":{"description":"the date the hold expires"},"timestamp":{"description":"date and time the hold was last updated"},"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"branchcode":{"description":"code of patron's home branch","type":["string","null"]}},"type":"object"},"description":"Created hold"},"400":{"schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"},"description":"Missing or wrong parameters"},"404":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Borrower not found"}},"produces":["application\/json"]},"get":{"parameters":[{"type":"integer","description":"Internal reserve identifier","name":"reserve_id","in":"query"},{"name":"borrowernumber","description":"Internal borrower identifier","type":"integer","in":"query"},{"in":"query","description":"Reserve date","type":"string","name":"reservedate"},{"type":"integer","description":"Internal biblio identifier","name":"biblionumber","in":"query"},{"name":"branchcode","description":"Branch code","type":"string","in":"query"},{"in":"query","name":"notificationdate","description":"Notification date","type":"string"},{"name":"reminderdate","type":"string","description":"Reminder date","in":"query"},{"in":"query","description":"Cancellation date","type":"string","name":"cancellationdate"},{"name":"reservenotes","description":"Reserve notes","type":"string","in":"query"},{"name":"priority","description":"Priority","type":"integer","in":"query"},{"type":"string","description":"Found status","name":"found","in":"query"},{"name":"timestamp","description":"Time of latest update","type":"string","in":"query"},{"name":"itemnumber","type":"integer","description":"Internal item identifier","in":"query"},{"name":"waitingdate","type":"string","description":"Date the item was marked as waiting for the patron","in":"query"},{"in":"query","name":"expirationdate","type":"string","description":"Date the hold expires"},{"name":"lowestPriority","type":"integer","description":"Lowest priority","in":"query"},{"in":"query","description":"Suspended","type":"integer","name":"suspend"},{"type":"string","description":"Suspended until","name":"suspend_until","in":"query"}],"tags":["borrowers","holds"],"operationId":"listHolds","responses":{"404":{"description":"Borrower not found","schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}}},"200":{"description":"A list of holds","schema":{"type":"array","items":{"type":"object","properties":{"biblionumber":{"description":"internally assigned biblio identifier","type":"string"},"priority":{"description":"where in the queue the patron sits"},"reservedate":{"description":"the date the hold was placed"},"notificationdate":{"description":"currently unused"},"reservenotes":{"description":"notes related to this hold"},"suspend_until":{"description":""},"suspend":{"description":""},"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"cancellationdate":{"description":"the date the hold was cancelled"},"reminderdate":{"description":"currently unused"},"itemnumber":{"description":"internally assigned item identifier","type":["string","null"]},"itemtype":{"type":["string","null"],"description":"If record level hold, the optional itemtype of the item the patron is requesting"},"lowestPriority":{"description":""},"reserve_id":{"description":"Internal hold identifier"},"expirationdate":{"description":"the date the hold expires"},"timestamp":{"description":"date and time the hold was last updated"},"found":{"description":"a one letter code defining what the status of the hold is after it has been confirmed"},"waitingdate":{"description":"the date the item was marked as waiting for the patron at the library"},"branchcode":{"description":"code of patron's home branch","type":["string","null"]}}}}}},"produces":["application\/json"]}},"\/patrons\/{borrowernumber}":{"get":{"parameters":[{"name":"borrowernumber","type":"integer","required":true,"description":"Internal patron identifier","in":"path"}],"produces":["application\/json"],"operationId":"getPatron","tags":["patrons"],"responses":{"404":{"description":"Patron not found","schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}}},"403":{"schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"},"description":"Access forbidden"},"200":{"description":"A patron","schema":{"type":"object","properties":{"mobile":{"type":["string","null"],"description":"the other phone number for patron's primary address"},"altcontactaddress2":{"type":["string","null"],"description":"the second address line for the alternate contact for the patron"},"gonenoaddress":{"description":"set to 1 if library marked this patron as having an unconfirmed address","type":["string","null"]},"lost":{"type":["string","null"],"description":"set to 1 if library marked this patron as having lost his card"},"othernames":{"description":"any other names associated with the patron","type":["string","null"]},"sms_provider_id":{"type":["string","null"],"description":"the provider of the mobile phone number defined in smsalertnumber"},"email":{"type":["string","null"],"description":"primary email address for patron's primary address"},"B_phone":{"description":"phone number for patron's alternate address","type":["string","null"]},"altcontactsurname":{"description":"surname or last name of the alternate contact for the patron","type":["string","null"]},"emailpro":{"type":["string","null"],"description":"secondary email address for patron's primary address"},"contacttitle":{"description":"used for children to include title of guarantor","type":["string","null"]},"B_streettype":{"type":["string","null"],"description":"street type of patron's alternate address"},"title":{"type":["string","null"],"description":"patron's title"},"zipcode":{"description":"zip or postal code of patron's primary address","type":["string","null"]},"B_address":{"description":"first address line of patron's alternate address","type":["string","null"]},"address2":{"type":["string","null"],"description":"second address line of patron's primary address"},"borrowernotes":{"description":"a note on the patron's account","type":["string","null"]},"contactnote":{"type":["string","null"],"description":"a note related to patron's alternate address"},"flags":{"description":"a number associated with the patron's permissions","type":["string","null"]},"updated_on":{"type":"string","description":"time of last change could be useful for synchronization with external systems (among others)"},"debarredcomment":{"description":"comment on the stop of the patron","type":["string","null"]},"B_email":{"type":["string","null"],"description":"email address for patron's alternate address"},"sort2":{"description":"a field that can be used for any information unique to the library","type":["string","null"]},"altcontactstate":{"type":["string","null"],"description":"the state for the alternate contact for the patron"},"initials":{"type":["string","null"],"description":"initials of the patron"},"B_state":{"type":["string","null"],"description":"state or province of patron's alternate address"},"B_country":{"description":"country of patron's alternate address","type":["string","null"]},"sort1":{"type":["string","null"],"description":"a field that can be used for any information unique to the library"},"categorycode":{"description":"code of patron's category","type":"string"},"debarred":{"type":["string","null"],"description":"until this date the patron can only check-in"},"B_streetnumber":{"type":["string","null"],"description":"street number of patron's alternate address"},"guarantorid":{"type":["string","null"],"description":"borrowernumber used for children or professionals to link them to guarantor or organizations"},"privacy":{"type":"string","description":"patron's privacy settings related to their reading history"},"B_city":{"type":["string","null"],"description":"city or town of patron's alternate address"},"firstname":{"type":["string","null"],"description":"patron's first name"},"userid":{"type":["string","null"],"description":"patron's login"},"dateofbirth":{"type":["string","null"],"description":"patron's date of birth"},"country":{"description":"country of patron's primary address","type":["string","null"]},"smsalertnumber":{"description":"the mobile phone number where the patron would like to receive notices (if SMS turned on)","type":["string","null"]},"altcontactaddress1":{"type":["string","null"],"description":"the first address line for the alternate contact for the patron"},"B_address2":{"description":"second address line of patron's alternate address","type":["string","null"]},"opacnote":{"type":["string","null"],"description":"a note on the patron's account visible in OPAC and staff client"},"cardnumber":{"type":["string","null"],"description":"library assigned user identifier"},"fax":{"description":"fax number for patron's primary address","type":["string","null"]},"altcontactfirstname":{"description":"first name of alternate contact for the patron","type":["string","null"]},"altcontactaddress3":{"description":"the city for the alternate contact for the patron","type":["string","null"]},"checkprevcheckout":{"type":"string","description":"produce a warning for this patron if this item has previously been checked out to this patron if 'yes', not if 'no', defer to category setting if 'inherit'"},"relationship":{"type":["string","null"],"description":"used for children to include the relationship to their guarantor"},"contactname":{"description":"used for children and professionals to include surname or last name of guarantor or organization name","type":["string","null"]},"streetnumber":{"type":["string","null"],"description":"street number of patron's primary address"},"phone":{"type":["string","null"],"description":"primary phone number for patron's primary address"},"streettype":{"type":["string","null"],"description":"street type of patron's primary address"},"surname":{"type":"string","description":"patron's last name"},"branchcode":{"description":"code of patron's home branch","type":["string","null"]},"contactfirstname":{"description":"used for children to include first name of guarantor","type":["string","null"]},"city":{"type":"string","description":"city or town of patron's primary address"},"altcontactzipcode":{"type":["string","null"],"description":"the zipcode for the alternate contact for the patron"},"password":{"type":["string","null"],"description":"patron's encrypted password"},"phonepro":{"description":"secondary phone number for patron's primary address","type":["string","null"]},"B_zipcode":{"description":"zip or postal code of patron's alternate address","type":["string","null"]},"altcontactcountry":{"description":"the country for the alternate contact for the patron","type":["string","null"]},"address":{"description":"first address line of patron's primary address","type":"string"},"sex":{"description":"patron's gender","type":["string","null"]},"altcontactphone":{"description":"the phone number for the alternate contact for the patron","type":["string","null"]},"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"dateexpiry":{"description":"date the patron's card is set to expire","type":["string","null"]},"privacy_guarantor_checkouts":{"type":"string","description":"controls if relatives can see this patron's checkouts"},"dateenrolled":{"description":"date the patron was added to Koha","type":["string","null"]},"state":{"type":["string","null"],"description":"state or province of patron's primary address"}}}}}}},"\/patrons":{"get":{"produces":["application\/json"],"operationId":"listPatrons","responses":{"200":{"description":"A list of patrons","schema":{"items":{"type":"object","properties":{"mobile":{"type":["string","null"],"description":"the other phone number for patron's primary address"},"altcontactaddress2":{"type":["string","null"],"description":"the second address line for the alternate contact for the patron"},"gonenoaddress":{"description":"set to 1 if library marked this patron as having an unconfirmed address","type":["string","null"]},"lost":{"type":["string","null"],"description":"set to 1 if library marked this patron as having lost his card"},"othernames":{"description":"any other names associated with the patron","type":["string","null"]},"sms_provider_id":{"type":["string","null"],"description":"the provider of the mobile phone number defined in smsalertnumber"},"email":{"type":["string","null"],"description":"primary email address for patron's primary address"},"B_phone":{"description":"phone number for patron's alternate address","type":["string","null"]},"altcontactsurname":{"description":"surname or last name of the alternate contact for the patron","type":["string","null"]},"emailpro":{"type":["string","null"],"description":"secondary email address for patron's primary address"},"contacttitle":{"description":"used for children to include title of guarantor","type":["string","null"]},"B_streettype":{"type":["string","null"],"description":"street type of patron's alternate address"},"title":{"type":["string","null"],"description":"patron's title"},"zipcode":{"description":"zip or postal code of patron's primary address","type":["string","null"]},"B_address":{"description":"first address line of patron's alternate address","type":["string","null"]},"address2":{"type":["string","null"],"description":"second address line of patron's primary address"},"borrowernotes":{"description":"a note on the patron's account","type":["string","null"]},"contactnote":{"type":["string","null"],"description":"a note related to patron's alternate address"},"flags":{"description":"a number associated with the patron's permissions","type":["string","null"]},"updated_on":{"type":"string","description":"time of last change could be useful for synchronization with external systems (among others)"},"debarredcomment":{"description":"comment on the stop of the patron","type":["string","null"]},"B_email":{"type":["string","null"],"description":"email address for patron's alternate address"},"sort2":{"description":"a field that can be used for any information unique to the library","type":["string","null"]},"altcontactstate":{"type":["string","null"],"description":"the state for the alternate contact for the patron"},"initials":{"type":["string","null"],"description":"initials of the patron"},"B_state":{"type":["string","null"],"description":"state or province of patron's alternate address"},"B_country":{"description":"country of patron's alternate address","type":["string","null"]},"sort1":{"type":["string","null"],"description":"a field that can be used for any information unique to the library"},"categorycode":{"description":"code of patron's category","type":"string"},"debarred":{"type":["string","null"],"description":"until this date the patron can only check-in"},"B_streetnumber":{"type":["string","null"],"description":"street number of patron's alternate address"},"guarantorid":{"type":["string","null"],"description":"borrowernumber used for children or professionals to link them to guarantor or organizations"},"privacy":{"type":"string","description":"patron's privacy settings related to their reading history"},"B_city":{"type":["string","null"],"description":"city or town of patron's alternate address"},"firstname":{"type":["string","null"],"description":"patron's first name"},"userid":{"type":["string","null"],"description":"patron's login"},"dateofbirth":{"type":["string","null"],"description":"patron's date of birth"},"country":{"description":"country of patron's primary address","type":["string","null"]},"smsalertnumber":{"description":"the mobile phone number where the patron would like to receive notices (if SMS turned on)","type":["string","null"]},"altcontactaddress1":{"type":["string","null"],"description":"the first address line for the alternate contact for the patron"},"B_address2":{"description":"second address line of patron's alternate address","type":["string","null"]},"opacnote":{"type":["string","null"],"description":"a note on the patron's account visible in OPAC and staff client"},"cardnumber":{"type":["string","null"],"description":"library assigned user identifier"},"fax":{"description":"fax number for patron's primary address","type":["string","null"]},"altcontactfirstname":{"description":"first name of alternate contact for the patron","type":["string","null"]},"altcontactaddress3":{"description":"the city for the alternate contact for the patron","type":["string","null"]},"checkprevcheckout":{"type":"string","description":"produce a warning for this patron if this item has previously been checked out to this patron if 'yes', not if 'no', defer to category setting if 'inherit'"},"relationship":{"type":["string","null"],"description":"used for children to include the relationship to their guarantor"},"contactname":{"description":"used for children and professionals to include surname or last name of guarantor or organization name","type":["string","null"]},"streetnumber":{"type":["string","null"],"description":"street number of patron's primary address"},"phone":{"type":["string","null"],"description":"primary phone number for patron's primary address"},"streettype":{"type":["string","null"],"description":"street type of patron's primary address"},"surname":{"type":"string","description":"patron's last name"},"branchcode":{"description":"code of patron's home branch","type":["string","null"]},"contactfirstname":{"description":"used for children to include first name of guarantor","type":["string","null"]},"city":{"type":"string","description":"city or town of patron's primary address"},"altcontactzipcode":{"type":["string","null"],"description":"the zipcode for the alternate contact for the patron"},"password":{"type":["string","null"],"description":"patron's encrypted password"},"phonepro":{"description":"secondary phone number for patron's primary address","type":["string","null"]},"B_zipcode":{"description":"zip or postal code of patron's alternate address","type":["string","null"]},"altcontactcountry":{"description":"the country for the alternate contact for the patron","type":["string","null"]},"address":{"description":"first address line of patron's primary address","type":"string"},"sex":{"description":"patron's gender","type":["string","null"]},"altcontactphone":{"description":"the phone number for the alternate contact for the patron","type":["string","null"]},"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"dateexpiry":{"description":"date the patron's card is set to expire","type":["string","null"]},"privacy_guarantor_checkouts":{"type":"string","description":"controls if relatives can see this patron's checkouts"},"dateenrolled":{"description":"date the patron was added to Koha","type":["string","null"]},"state":{"type":["string","null"],"description":"state or province of patron's primary address"}}},"type":"array"}},"403":{"description":"Access forbidden","schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"}}},"tags":["patrons"]}},"\/auth\/session":{"delete":{"parameters":[{"in":"body","name":"session","description":"The CGISESSID Cookie used to authenticate a session","required":false,"schema":{"properties":{"sessionid":{"type":"string","description":"The Koha sessionid"}},"required":["sessionid"],"type":"object"}}],"description":"Logouts user from Koha by marking session as expired. sessionid is optional, if not given, logs out currently logged in user","produces":["application\/json"],"responses":{"401":{"schema":{"properties":{"error":{"description":"Error message","type":"string"}},"type":"object"},"description":"Bad session id"},"200":{"schema":{"type":"object"},"description":"Successfully logged out"}},"operationId":"logoutAuth","tags":["auth"],"summary":"Logout from Koha."},"post":{"responses":{"201":{"description":"A borrower with SSO-relevant fields","schema":{"properties":{"firstname":{"type":["string","null"],"description":"patron's first name"},"surname":{"type":"string","description":"patron's last name"},"sessionid":{"description":"Koha Session identifier","type":"string"},"email":{"type":["string","null"],"description":"primary email address for patron's primary address"},"permissions":{"items":{"type":"string"},"type":"array","description":"patron's permissions"},"borrowernumber":{"type":"string","description":"internally assigned user identifier"}},"type":"object"}},"401":{"schema":{"type":"object","properties":{"error":{"description":"Error message","type":"string"}}},"description":"Bad username\/cardnumber and\/or password"}},"operationId":"loginAuth","tags":["auth"],"summary":"Login to Koha and get a session cookie","description":"Makes a 'normal' username + password login to Koha, and returns the sessionid you need put to the CGISESSID-cookie. Koha uses this cookie to track a session.\nBe aware that the authenticated session most probably is IP-locked so authenticating from one IP and passing the session to another wont work.","produces":["application\/json"],"parameters":[{"type":"string","required":false,"description":"Borrower's card's barcode\/identifier","name":"cardnumber","in":"formData"},{"in":"formData","type":"string","required":false,"description":"The userid of the Borrower, unique value","name":"userid"},{"in":"formData","required":true,"type":"string","name":"password"}]}}},"parameters":{"useridPostParam":{"in":"formData","name":"userid","description":"The userid of the Borrower, unique value","required":false,"type":"string"},"sessionidBodyParam":{"name":"session","schema":{"properties":{"sessionid":{"type":"string","description":"The Koha sessionid"}},"required":["sessionid"],"type":"object"},"description":"The CGISESSID Cookie used to authenticate a session","required":false,"in":"body"},"holdIdPathParam":{"in":"path","name":"reserve_id","description":"Internal hold identifier","required":true,"type":"integer"},"cardnumberPostParam":{"in":"formData","name":"cardnumber","type":"string","required":false,"description":"Borrower's card's barcode\/identifier"},"borrowernumberPathParam":{"in":"path","type":"integer","description":"Internal patron identifier","required":true,"name":"borrowernumber"},"passwordPostParam":{"in":"formData","required":true,"type":"string","name":"password"},"borrowernumberQueryParam":{"in":"query","description":"Internal borrower identifier","type":"integer","name":"borrowernumber"}},"x-primitives":{"borrowernumber":{"description":"internally assigned user identifier","type":"string"},"email":{"description":"primary email address for patron's primary address","type":["string","null"]},"cardnumber":{"description":"library assigned user identifier","type":["string","null"]},"branchcode":{"type":["string","null"],"description":"code of patron's home branch"},"surname":{"description":"patron's last name","type":"string"},"firstname":{"description":"patron's first name","type":["string","null"]},"phone":{"description":"primary phone number for patron's primary address","type":["string","null"]},"biblionumber":{"description":"internally assigned biblio identifier","type":"string"},"itemnumber":{"description":"internally assigned item identifier","type":["string","null"]},"reserve_id":{"description":"Internal hold identifier"}},"info":{"version":"1","contact":{"url":"http:\/\/koha-community.org\/","name":"Koha Team"},"title":"Koha REST API","license":{"url":"http:\/\/www.gnu.org\/licenses\/gpl.txt","name":"GPL v3"}}} \ No newline at end of file diff --git a/t/db_dependent/api/v1/auth.t b/t/db_dependent/api/v1/auth.t new file mode 100644 index 0000000..56d1a87 --- /dev/null +++ b/t/db_dependent/api/v1/auth.t @@ -0,0 +1,140 @@ +#!/usr/bin/env perl + +# Copyright 2016 Koha-Suomi +# +# 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 => 38; +use Test::Mojo; + +use t::lib::TestBuilder; + +use C4::Auth; +use C4::Context; +use Koha::AuthUtils; + +my $builder = t::lib::TestBuilder->new(); + +my $dbh = C4::Context->dbh; +$dbh->{AutoCommit} = 0; +$dbh->{RaiseError} = 1; + +$ENV{REMOTE_ADDR} = '127.0.0.1'; +my $t = Test::Mojo->new('Koha::REST::V1'); + +my $categorycode = $builder->build({ source => 'Category' })->{ categorycode }; +my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; +my $password = "2anxious? if someone finds out"; + +my $borrower = $builder->build({ + source => 'Borrower', + value => { + branchcode => $branchcode, + categorycode => $categorycode, + password => Koha::AuthUtils::hash_password($password), + } +}); + +my $auth_by_userid = { + userid => $borrower->{userid}, + password => $password, +}; +my $auth_by_cardnumber = { + cardnumber => $borrower->{cardnumber}, + password => $password, +}; +my $invalid_login = { + userid => $borrower->{userid}, + password => "please let me in", +}; +my $invalid_login2 = { + cardnumber => $borrower->{cardnumber}, + password => "my password is password, don't tell anyone", +}; + +my $tx = $t->ua->build_tx(POST => '/api/v1/auth/session' => form => $auth_by_userid); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(201) + ->json_is('/firstname', $borrower->{firstname}) + ->json_is('/surname', $borrower->{surname}) + ->json_is('/borrowernumber', $borrower->{borrowernumber}) + ->json_is('/email', $borrower->{email}) + ->json_has('/sessionid'); + +$tx = $t->ua->build_tx(POST => '/api/v1/auth/session' => form => $auth_by_cardnumber); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(201) + ->json_is('/firstname', $borrower->{firstname}) + ->json_is('/surname', $borrower->{surname}) + ->json_is('/borrowernumber', $borrower->{borrowernumber}) + ->json_is('/email', $borrower->{email}) + ->json_has('/sessionid'); +my $sessionid = $tx->res->json->{sessionid}; + +$tx = $t->ua->build_tx(POST => '/api/v1/auth/session' => form => $invalid_login); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(401) + ->json_is('/error', "Login failed."); + +$tx = $t->ua->build_tx(POST => '/api/v1/auth/session' => form => $invalid_login2); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(401) + ->json_is('/error', "Login failed."); + +$tx = $t->ua->build_tx(DELETE => '/api/v1/auth/session' => json => { sessionid => $sessionid."123" }); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(401) + ->json_is('/error', "Invalid session id."); + +my ($sess_status, $sid) = C4::Auth::check_cookie_auth($sessionid); +is($sess_status, "ok", "Session is valid before logging out."); +$tx = $t->ua->build_tx(DELETE => '/api/v1/auth/session' => json => { sessionid => $sessionid }); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(200); + +($sess_status, $sid) = C4::Auth::check_cookie_auth($sessionid); +isnt($sess_status, "ok", "Session is not valid after logging out."); + +$tx = $t->ua->build_tx(POST => '/api/v1/auth/session' => form => $auth_by_cardnumber); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(201) + ->json_is('/firstname', $borrower->{firstname}) + ->json_is('/surname', $borrower->{surname}) + ->json_is('/borrowernumber', $borrower->{borrowernumber}) + ->json_is('/email', $borrower->{email}) + ->json_has('/sessionid'); +$sessionid = $tx->res->json->{sessionid}; + +($sess_status, $sid) = C4::Auth::check_cookie_auth($sessionid); +is($sess_status, "ok", "Session is valid before logging out."); +$tx = $t->ua->build_tx(DELETE => '/api/v1/auth/session'); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(200); + +($sess_status, $sid) = C4::Auth::check_cookie_auth($sessionid); +isnt($sess_status, "ok", "Session is not valid after logging out."); + +$dbh->rollback; -- 2.1.4