3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 3 of the License, or (at your option) any later
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along
15 # with Koha; if not, write to the Free Software Foundation, Inc.,
16 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 use Module::Load::Conditional qw(can_load);
32 use t::lib::TestBuilder;
34 my $t = Test::Mojo->new('Koha::REST::V1');
35 my $schema = Koha::Database->new->schema;
36 my $builder = t::lib::TestBuilder->new();
38 if ( can_load( modules => { 'Net::OAuth2::AuthorizationServer' => undef } ) ) {
42 plan skip_all => 'Net::OAuth2::AuthorizationServer not available';
45 subtest '/oauth/token tests' => sub {
49 t::lib::Mocks::mock_preference('RESTOAuth2ClientCredentials', 0);
51 # Missing parameter grant_type
52 $t->post_ok('/api/v1/oauth/token')
56 $t->post_ok('/api/v1/oauth/token', form => { grant_type => 'password' })
58 ->json_is({error => 'Unimplemented grant type'});
60 t::lib::Mocks::mock_preference('RESTOAuth2ClientCredentials', 1);
62 # No client_id/client_secret
63 $t->post_ok('/api/v1/oauth/token', form => { grant_type => 'client_credentials' })
65 ->json_is({error => 'unauthorized_client'});
67 subtest 'Client credentials in body' => sub {
71 run_oauth_tests( 'body' );
74 subtest 'Client credentials in Authorization header' => sub {
79 run_oauth_tests( 'header' );
83 subtest 'Net::OAuth2::AuthorizationServer missing tests' => sub {
87 my $load_conditional = Test::MockModule->new('Module::Load::Conditional');
89 # Enable the client credentials grant syspref
90 t::lib::Mocks::mock_preference( 'RESTOAuth2ClientCredentials', 1 );
92 my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { flags => 2**4 } });
93 my $api_key = Koha::ApiKey->new({ patron_id => $patron->id, description => 'blah' })->store;
96 grant_type => 'client_credentials',
97 client_id => $api_key->client_id,
98 client_secret => $api_key->secret
101 $t->post_ok( '/api/v1/oauth/token', form => $form_data )->status_is(200)
102 ->json_is( '/expires_in' => 3600 )->json_is( '/token_type' => 'Bearer' )
103 ->json_has('/access_token');
105 my $access_token = $t->tx->res->json->{access_token};
107 $load_conditional->mock( 'can_load', sub { return 0; } );
109 my $tx = $t->ua->build_tx( GET => '/api/v1/patrons' );
110 $tx->req->headers->authorization("Bearer $access_token");
114 $t->post_ok( '/api/v1/oauth/token', form => $form_data )
116 ->json_is( { error => 'Unimplemented grant type' } );
120 sub run_oauth_tests {
121 my ( $test_case ) = @_;
123 $schema->storage->txn_begin;
125 my $patron = $builder->build_object({
126 class => 'Koha::Patrons',
128 flags => 0 # no permissions
132 my $api_key = Koha::ApiKey->new({ patron_id => $patron->id, description => 'blah' })->store;
134 t::lib::Mocks::mock_preference( 'RESTOAuth2ClientCredentials', 1 );
137 my $client_id = $api_key->client_id;
138 my $client_secret = $api_key->secret;
140 if ( $test_case eq 'header' ) {
142 $formData = { grant_type => 'client_credentials' };
144 $t->post_ok("//$client_id:$client_secret@/api/v1/oauth/token", form => $formData)
146 ->json_is('/expires_in' => 3600)
147 ->json_is('/token_type' => 'Bearer')
148 ->json_has('/access_token');
152 grant_type => 'client_credentials',
153 client_id => $api_key->client_id,
154 client_secret => $api_key->secret
157 $t->post_ok('/api/v1/oauth/token', form => $formData)
159 ->json_is('/expires_in' => 3600)
160 ->json_is('/token_type' => 'Bearer')
161 ->json_has('/access_token');
164 my $access_token = $t->tx->res->json->{access_token};
166 # Without access token, it returns 401
167 $t->get_ok('/api/v1/patrons')->status_is(401);
169 # With access token, but without permissions, it returns 403
170 my $tx = $t->ua->build_tx(GET => '/api/v1/patrons');
171 $tx->req->headers->authorization("Bearer $access_token");
172 $t->request_ok($tx)->status_is(403);
174 # With access token and permissions, it returns 200
175 $patron->flags(2**4)->store;
176 $tx = $t->ua->build_tx(GET => '/api/v1/patrons');
177 $tx->req->headers->authorization("Bearer $access_token");
178 $t->request_ok($tx)->status_is(200);
181 my $token = Koha::OAuthAccessTokens->find($access_token);
182 $token->expires( time - 1 )->store;
183 $tx = $t->ua->build_tx( GET => '/api/v1/patrons' );
184 $tx->req->headers->authorization("Bearer $access_token");
189 $api_key->active(0)->store;
191 if ( $test_case eq 'header' ) {
192 $t->post_ok("//$client_id:$client_secret@/api/v1/oauth/token", form => $formData)
194 ->json_is({ error => 'unauthorized_client' });
196 $t->post_ok('/api/v1/oauth/token', form => $formData)
198 ->json_is({ error => 'unauthorized_client' });
201 # disable client credentials grant
202 t::lib::Mocks::mock_preference('RESTOAuth2ClientCredentials', 0);
205 $api_key->active(1)->store;
208 if ( $test_case eq 'header' ) {
209 $t->post_ok("//$client_id:$client_secret@/api/v1/oauth/token", form => $formData )
211 ->json_is({ error => 'Unimplemented grant type' });
214 $t->post_ok('/api/v1/oauth/token', form => $formData )
216 ->json_is({ error => 'Unimplemented grant type' });
219 $schema->storage->txn_rollback;