Bugzilla – Attachment 55146 Details for
Bug 17004
REST API: add route to authenticate patron (Single Sign On - SSO)
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 17004: Add API route to authenticate patron (CGISESSID) including logout
Bug-17004-Add-API-route-to-authenticate-patron-CGI.patch (text/plain), 16.06 KB, created by
Benjamin Rokseth
on 2016-09-02 21:57:56 UTC
(
hide
)
Description:
Bug 17004: Add API route to authenticate patron (CGISESSID) including logout
Filename:
MIME Type:
Creator:
Benjamin Rokseth
Created:
2016-09-02 21:57:56 UTC
Size:
16.06 KB
patch
obsolete
>From e9f9a96c4e45b0f8748b616114b49a569d6a2cdd Mon Sep 17 00:00:00 2001 >From: Lari Taskula <larit@student.uef.fi> >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" => <your_user_id> and "password" => <your_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 <benjamin.rokseth@kul.oslo.kommune.no> >--- > 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 +++++++++++++ > t/db_dependent/api/v1/auth.t | 140 ++++++++++++++++++++++++++++++ > 9 files changed, 390 insertions(+) > 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/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
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 17004
:
54028
|
54068
|
54682
|
54683
|
54684
|
54711
|
54953
|
54957
| 55146