diff --git a/cgi/display.pl b/cgi/display.pl index 05ff5585df12c..c10e864a03675 100755 --- a/cgi/display.pl +++ b/cgi/display.pl @@ -31,7 +31,6 @@ use ProductOpener::Display qw/:all/; use ProductOpener::Users qw/:all/; use ProductOpener::Lang qw/:all/; -use ProductOpener::API qw/:all/; use CGI qw/:cgi :form escapeHTML/; use URI::Escape::XS; @@ -69,9 +68,6 @@ read_request_body($request_ref); decode_json_request_body($request_ref); } - - # The API V3 requests supports Bearer Authentication headers. Copy the bearer token to request_ref for auth. - process_auth_header($request_ref, $r); } if (($env_query_string !~ /^\/?api\/v(3(\.\d+)?)\//) or ($request_ref->{method} !~ /^(POST|PUT|PATCH)$/)) { diff --git a/lib/ProductOpener/API.pm b/lib/ProductOpener/API.pm index 3bc1086c87863..e362b3b54634c 100644 --- a/lib/ProductOpener/API.pm +++ b/lib/ProductOpener/API.pm @@ -853,14 +853,14 @@ Reference to the Apache2 request object =head3 Return value -None +1 if the user has been signed in, -1 if the Bearer token was invalid, 0 otherwise. =cut sub process_auth_header ($request_ref, $r) { my $token = _read_auth_header($request_ref, $r); unless ($token) { - return; + return 0; } my $access_token; @@ -879,7 +879,7 @@ sub process_auth_header ($request_ref, $r) { impact => {id => 'failure'}, } ); - return; + return -1; } $request_ref->{access_token} = $access_token; @@ -904,7 +904,7 @@ sub process_auth_header ($request_ref, $r) { param('user_session', $user_session); init_user($request_ref); - return; + return 1; } =head2 _read_auth_header ( $request_ref, $r ) diff --git a/lib/ProductOpener/Display.pm b/lib/ProductOpener/Display.pm index b4ba7f3a6602e..eca9b792d21b4 100644 --- a/lib/ProductOpener/Display.pm +++ b/lib/ProductOpener/Display.pm @@ -1,7 +1,7 @@ # This file is part of Product Opener. # # Product Opener -# Copyright (C) 2011-2023 Association Open Food Facts +# Copyright (C) 2011-2024 Association Open Food Facts # Contact: contact@openfoodfacts.org # Address: 21 rue des Iles, 94100 Saint-Maur des Fossés, France # @@ -810,6 +810,21 @@ sub init_request ($request_ref = {}) { } ) if $log->is_debug(); + my $signed_in_oidc = process_auth_header($request_ref, $r); + if ($signed_in_oidc < 0) { + # We were sent a bad bearer token + # Otherwise we return an error page in HTML (including for v0 / v1 / v2 API queries) + if (not((defined $request_ref->{api_version}) and ($request_ref->{api_version} >= 3)) + and (not($r->uri() =~ /\/cgi\/auth\.pl/))) + { + $log->debug( + "init_request - init_user error - display error page", + {init_user_error => $request_ref->{init_user_error}} + ) if $log->is_debug(); + display_error_and_exit($signed_in_oidc, 403); + } + } + my $error = ProductOpener::Users::init_user($request_ref); if ($error) { # We were sent bad user_id / password credentials diff --git a/tests/integration/api_v2_product_write.t b/tests/integration/api_v2_product_write.t index 9308d63ea1a02..2b21fa3834d19 100644 --- a/tests/integration/api_v2_product_write.t +++ b/tests/integration/api_v2_product_write.t @@ -3,9 +3,13 @@ use ProductOpener::PerlStandards; use Test::More; +use Log::Any::Adapter 'TAP'; +use Log::Any qw($log); + use ProductOpener::APITest qw/:all/; use ProductOpener::Test qw/:all/; use ProductOpener::TestDefaults qw/:all/; +use ProductOpener::Auth qw/get_token_using_password_credentials/; use File::Basename "dirname"; @@ -22,6 +26,9 @@ my $ua = new_client(); my %create_user_args = (%default_user_form, (email => 'bob@gmail.com')); create_user($ua, \%create_user_args); +my $token = get_token_using_password_credentials('tests', $test_password)->{access_token}; +$log->debug('test token', {token => $token}) if $log->is_debug(); + # Note: expected results are stored in json files, see execute_api_tests my $tests_ref = [ { @@ -70,11 +77,58 @@ my $tests_ref = [ nutriment_sugars => '12.5', } }, + # Test authentication - OAuth token + { + test_case => 'post-product-oauth-token', + method => 'POST', + path => '/cgi/product_jqm_multilingual.pl', + form => { + cc => "be", + lc => "fr", + code => "1234567890005", + product_name => "Product name", + categories => "Cookies", + quantity => "250 g", + serving_size => '20 g', + ingredients_text_fr => "Farine de blé, eau, sel, sucre", + labels => "Bio, Max Havelaar", + nutriment_salt => '50.2', + nutriment_salt_unit => 'mg', + nutriment_sugars => '12.5', + }, + headers_in => { + 'Authorization' => 'Bearer ' . $token, + }, + }, { test_case => 'get-product-auth-good-password', method => 'GET', path => '/api/v2/product/1234567890002', }, + { + test_case => 'post-product-auth-bad-oauth-token', + method => 'POST', + path => '/cgi/product_jqm_multilingual.pl', + form => { + cc => "be", + lc => "fr", + code => "1234567890006", + product_name => "Product name", + categories => "Cookies", + quantity => "250 g", + serving_size => '20 g', + ingredients_text_fr => "Farine de blé, eau, sel, sucre", + labels => "Bio, Max Havelaar", + nutriment_salt => '50.2', + nutriment_salt_unit => 'mg', + nutriment_sugars => '12.5', + }, + headers_in => { + 'Authorization' => 'Bearer 4711', + }, + expected_type => "html", + expected_status_code => 403, + }, { test_case => 'post-product-auth-bad-user-password', method => 'POST', diff --git a/tests/integration/expected_test_results/api_v2_product_write/post-product-oauth-token.json b/tests/integration/expected_test_results/api_v2_product_write/post-product-oauth-token.json new file mode 100644 index 0000000000000..39d4261a9d3f0 --- /dev/null +++ b/tests/integration/expected_test_results/api_v2_product_write/post-product-oauth-token.json @@ -0,0 +1,4 @@ +{ + "status" : 1, + "status_verbose" : "fields saved" +} diff --git a/tests/integration/expected_test_results/upload_images/post-product-image-good-oauth-token.json b/tests/integration/expected_test_results/upload_images/post-product-image-good-oauth-token.json new file mode 100644 index 0000000000000..1a7bedc0fa465 --- /dev/null +++ b/tests/integration/expected_test_results/upload_images/post-product-image-good-oauth-token.json @@ -0,0 +1,20 @@ +{ + "code" : "1234567890017", + "files" : [ + { + "code" : "1234567890017", + "filename" : "", + "name" : "1234567890017", + "thumbnailUrl" : "/images/products/123/456/789/0017/1.100.jpg", + "url" : "/product/1234567890017" + } + ], + "image" : { + "crop_url" : "1.400.jpg", + "imgid" : 1, + "thumb_url" : "1.100.jpg" + }, + "imagefield" : "front_en", + "imgid" : 1, + "status" : "status ok" +} diff --git a/tests/integration/upload_images.t b/tests/integration/upload_images.t index 6f65dd8f794bb..19899e6c698dd 100644 --- a/tests/integration/upload_images.t +++ b/tests/integration/upload_images.t @@ -3,9 +3,13 @@ use ProductOpener::PerlStandards; use Test::More; +use Log::Any::Adapter 'TAP'; +use Log::Any qw($log); + use ProductOpener::APITest qw/:all/; use ProductOpener::Test qw/:all/; use ProductOpener::TestDefaults qw/:all/; +use ProductOpener::Auth qw/get_token_using_password_credentials/; use File::Basename "dirname"; @@ -19,6 +23,14 @@ remove_all_products(); my $sample_products_images_path = dirname(__FILE__) . "/inputs/upload_images"; +my $ua = new_client(); + +my %create_user_args = (%default_user_form, (email => 'bob@gmail.com')); +create_user($ua, \%create_user_args); + +my $token = get_token_using_password_credentials('tests', $test_password)->{access_token}; +$log->debug('test token', {token => $token}) if $log->is_debug(); + my $tests_ref = [ { test_case => 'post-product-image', @@ -118,7 +130,35 @@ my $tests_ref = [ expected_status_code => 200, }, - + { + test_case => 'post-product-image-good-oauth-token', + method => 'POST', + path => '/cgi/product_image_upload.pl', + form => { + code => "1234567890017", + imagefield => "front_en", + imgupload_front_en => ["$sample_products_images_path/1.jpg", '1.jpg'], + }, + headers_in => { + 'Authorization' => 'Bearer ' . $token, + }, + expected_status_code => 200, + }, + { + test_case => 'post-product-image-bad-oauth-token', + method => 'POST', + path => '/cgi/product_image_upload.pl', + form => { + code => "1234567890018", + imagefield => "front_en", + imgupload_front_en => ["$sample_products_images_path/1.jpg", '1.jpg'], + }, + headers_in => { + 'Authorization' => 'Bearer 4711', + }, + expected_status_code => 403, + expected_type => 'html' + }, ]; execute_api_tests(__FILE__, $tests_ref);