From 13a24bb0375db9529934f40e26fd822c05181e6a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 6 Feb 2025 16:22:29 +1100 Subject: [PATCH 1/5] Clear invalid OIDC tokens --- .../dfc_provider/app/services/dfc_request.rb | 3 + .../spec/services/dfc_request_spec.rb | 12 +++ .../clears_invalid_refresh_tokens.yml | 102 ++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 spec/fixtures/vcr_cassettes/DfcRequest/clears_invalid_refresh_tokens.yml diff --git a/engines/dfc_provider/app/services/dfc_request.rb b/engines/dfc_provider/app/services/dfc_request.rb index c976a697877..10c36ef2224 100644 --- a/engines/dfc_provider/app/services/dfc_request.rb +++ b/engines/dfc_provider/app/services/dfc_request.rb @@ -81,5 +81,8 @@ def refresh_access_token! token: token.access_token, refresh_token: token.refresh_token ) + rescue Rack::OAuth2::Client::Error + @user.oidc_account.update!(token: nil, refresh_token: nil) + raise end end diff --git a/engines/dfc_provider/spec/services/dfc_request_spec.rb b/engines/dfc_provider/spec/services/dfc_request_spec.rb index 0e6fbfb8d34..af459f78c4f 100644 --- a/engines/dfc_provider/spec/services/dfc_request_spec.rb +++ b/engines/dfc_provider/spec/services/dfc_request_spec.rb @@ -63,6 +63,18 @@ # The absence of errors makes this test pass. end + it "clears invalid refresh tokens", vcr: true do + stub_request(:get, "http://example.net/api").to_return(status: 401) + + account.refresh_token = "some-invalid-token" + account.updated_at = 1.day.ago + + expect { api.call("http://example.net/api") } + .to raise_error(Rack::OAuth2::Client::Error) + + expect(account.refresh_token).to eq nil + end + it "refreshes the access token and retrieves the FDC catalog", vcr: true do # A refresh is only attempted if the token is stale. account.uid = "testdfc@protonmail.com" diff --git a/spec/fixtures/vcr_cassettes/DfcRequest/clears_invalid_refresh_tokens.yml b/spec/fixtures/vcr_cassettes/DfcRequest/clears_invalid_refresh_tokens.yml new file mode 100644 index 00000000000..f099a659540 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/DfcRequest/clears_invalid_refresh_tokens.yml @@ -0,0 +1,102 @@ +--- +http_interactions: +- request: + method: get + uri: https://login.lescommuns.org/auth/realms/data-food-consortium/.well-known/openid-configuration + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - SWD 2.0.3 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 06 Feb 2025 05:16:24 GMT + Content-Type: + - application/json;charset=UTF-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Vary: + - Accept-Encoding + Set-Cookie: + - AUTH_SESSION_ID=1738818985.41.76556.871008|78230f584c0d7db97d376e98de5321dc; + Path=/; Secure; HttpOnly + Cache-Control: + - no-cache, must-revalidate, no-transform, no-store + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + body: + encoding: ASCII-8BIT + string: '{"issuer":"https://login.lescommuns.org/auth/realms/data-food-consortium","authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth","token_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token","introspection_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token/introspect","userinfo_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/userinfo","end_session_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/logout","frontchannel_logout_session_supported":true,"frontchannel_logout_supported":true,"jwks_uri":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/certs","check_session_iframe":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/login-status-iframe.html","grant_types_supported":["authorization_code","implicit","refresh_token","password","client_credentials","urn:openid:params:grant-type:ciba","urn:ietf:params:oauth:grant-type:device_code"],"acr_values_supported":["0","1"],"response_types_supported":["code","none","id_token","token","id_token + token","code id_token","code token","code id_token token"],"subject_types_supported":["public","pairwise"],"id_token_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"id_token_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"id_token_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"userinfo_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512","none"],"userinfo_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"userinfo_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"request_object_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512","none"],"request_object_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"request_object_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"response_modes_supported":["query","fragment","form_post","query.jwt","fragment.jwt","form_post.jwt","jwt"],"registration_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/clients-registrations/openid-connect","token_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"token_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"introspection_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"introspection_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"authorization_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"authorization_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"authorization_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"claims_supported":["aud","sub","iss","auth_time","name","given_name","family_name","preferred_username","email","acr"],"claim_types_supported":["normal"],"claims_parameter_supported":true,"scopes_supported":["openid","microprofile-jwt","phone","roles","profile","email","address","web-origins","acr","offline_access","ReadProduct"],"request_parameter_supported":true,"request_uri_parameter_supported":true,"require_request_uri_registration":true,"code_challenge_methods_supported":["plain","S256"],"tls_client_certificate_bound_access_tokens":true,"revocation_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/revoke","revocation_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"revocation_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"device_authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth/device","backchannel_token_delivery_modes_supported":["poll","ping"],"backchannel_authentication_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/ciba/auth","backchannel_authentication_request_signing_alg_values_supported":["PS384","ES384","RS384","ES256","RS256","ES512","PS256","PS512","RS512"],"require_pushed_authorization_requests":false,"pushed_authorization_request_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/par/request","mtls_endpoint_aliases":{"token_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token","revocation_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/revoke","introspection_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token/introspect","device_authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth/device","registration_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/clients-registrations/openid-connect","userinfo_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/userinfo","pushed_authorization_request_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/par/request","backchannel_authentication_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/ciba/auth"},"authorization_response_iss_parameter_supported":true}' + recorded_at: Thu, 06 Feb 2025 05:16:24 GMT +- request: + method: post + uri: https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token + body: + encoding: UTF-8 + string: grant_type=refresh_token&refresh_token=some-invalid-token + headers: + User-Agent: + - Rack::OAuth2 (2.2.1) + Authorization: + - "" + Content-Type: + - application/x-www-form-urlencoded + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 400 + message: Bad Request + headers: + Date: + - Thu, 06 Feb 2025 05:16:25 GMT + Content-Type: + - application/json + Content-Length: + - '69' + Connection: + - keep-alive + Set-Cookie: + - AUTH_SESSION_ID=1738818986.62.77902.27989|78230f584c0d7db97d376e98de5321dc; + Path=/; Secure; HttpOnly + Cache-Control: + - no-store + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + body: + encoding: UTF-8 + string: '{"error":"invalid_grant","error_description":"Invalid refresh token"}' + recorded_at: Thu, 06 Feb 2025 05:16:25 GMT +recorded_with: VCR 6.2.0 From 0f9610ca5d77e7d841cbfc8905892906c3f6b27a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 6 Feb 2025 16:56:13 +1100 Subject: [PATCH 2/5] Direct people to refresh OIDC connection Not the best UX but the easiest next step to implement. Next we should: * Include link in error message instead of redirecting straight there. Otherwise users may feel disoriented. * Provide a custom error message? --- .../admin/dfc_product_imports_controller.rb | 3 + config/locales/en.yml | 3 + .../prompts_to_refresh_OIDC_connection.yml | 102 ++++++++++++++++++ spec/system/admin/dfc_product_import_spec.rb | 21 ++++ 4 files changed, 129 insertions(+) create mode 100644 spec/fixtures/vcr_cassettes/DFC_Product_Import/prompts_to_refresh_OIDC_connection.yml diff --git a/app/controllers/admin/dfc_product_imports_controller.rb b/app/controllers/admin/dfc_product_imports_controller.rb index 58239786241..37adc70557e 100644 --- a/app/controllers/admin/dfc_product_imports_controller.rb +++ b/app/controllers/admin/dfc_product_imports_controller.rb @@ -40,6 +40,9 @@ def index ActionController::ParameterMissing => e flash[:error] = e.message redirect_to admin_product_import_path + rescue Rack::OAuth2::Client::Error + flash[:error] = t(".connection_invalid") + redirect_to admin_oidc_settings_path end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 07a974d49a4..36b8c93139b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -846,6 +846,9 @@ en: map: Map dfc_product_imports: + connection_invalid: | + Connecting with your OIDC account failed. + Please refresh your OIDC connection. index: title: "Importing a DFC product catalog" imported_products: "Imported products:" diff --git a/spec/fixtures/vcr_cassettes/DFC_Product_Import/prompts_to_refresh_OIDC_connection.yml b/spec/fixtures/vcr_cassettes/DFC_Product_Import/prompts_to_refresh_OIDC_connection.yml new file mode 100644 index 00000000000..68addbdf120 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/DFC_Product_Import/prompts_to_refresh_OIDC_connection.yml @@ -0,0 +1,102 @@ +--- +http_interactions: +- request: + method: get + uri: https://login.lescommuns.org/auth/realms/data-food-consortium/.well-known/openid-configuration + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - SWD 2.0.3 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 06 Feb 2025 05:53:45 GMT + Content-Type: + - application/json;charset=UTF-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Vary: + - Accept-Encoding + Set-Cookie: + - AUTH_SESSION_ID=1738821226.43.77473.555543|78230f584c0d7db97d376e98de5321dc; + Path=/; Secure; HttpOnly + Cache-Control: + - no-cache, must-revalidate, no-transform, no-store + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + body: + encoding: ASCII-8BIT + string: '{"issuer":"https://login.lescommuns.org/auth/realms/data-food-consortium","authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth","token_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token","introspection_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token/introspect","userinfo_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/userinfo","end_session_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/logout","frontchannel_logout_session_supported":true,"frontchannel_logout_supported":true,"jwks_uri":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/certs","check_session_iframe":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/login-status-iframe.html","grant_types_supported":["authorization_code","implicit","refresh_token","password","client_credentials","urn:openid:params:grant-type:ciba","urn:ietf:params:oauth:grant-type:device_code"],"acr_values_supported":["0","1"],"response_types_supported":["code","none","id_token","token","id_token + token","code id_token","code token","code id_token token"],"subject_types_supported":["public","pairwise"],"id_token_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"id_token_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"id_token_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"userinfo_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512","none"],"userinfo_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"userinfo_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"request_object_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512","none"],"request_object_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"request_object_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"response_modes_supported":["query","fragment","form_post","query.jwt","fragment.jwt","form_post.jwt","jwt"],"registration_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/clients-registrations/openid-connect","token_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"token_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"introspection_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"introspection_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"authorization_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"authorization_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"authorization_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"claims_supported":["aud","sub","iss","auth_time","name","given_name","family_name","preferred_username","email","acr"],"claim_types_supported":["normal"],"claims_parameter_supported":true,"scopes_supported":["openid","microprofile-jwt","phone","roles","profile","email","address","web-origins","acr","offline_access","ReadProduct"],"request_parameter_supported":true,"request_uri_parameter_supported":true,"require_request_uri_registration":true,"code_challenge_methods_supported":["plain","S256"],"tls_client_certificate_bound_access_tokens":true,"revocation_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/revoke","revocation_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"revocation_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"device_authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth/device","backchannel_token_delivery_modes_supported":["poll","ping"],"backchannel_authentication_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/ciba/auth","backchannel_authentication_request_signing_alg_values_supported":["PS384","ES384","RS384","ES256","RS256","ES512","PS256","PS512","RS512"],"require_pushed_authorization_requests":false,"pushed_authorization_request_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/par/request","mtls_endpoint_aliases":{"token_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token","revocation_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/revoke","introspection_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token/introspect","device_authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth/device","registration_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/clients-registrations/openid-connect","userinfo_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/userinfo","pushed_authorization_request_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/par/request","backchannel_authentication_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/ciba/auth"},"authorization_response_iss_parameter_supported":true}' + recorded_at: Thu, 06 Feb 2025 05:53:45 GMT +- request: + method: post + uri: https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token + body: + encoding: UTF-8 + string: grant_type=refresh_token&refresh_token=something-expired-or-invalid + headers: + User-Agent: + - Rack::OAuth2 (2.2.1) + Authorization: + - "" + Content-Type: + - application/x-www-form-urlencoded + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 400 + message: Bad Request + headers: + Date: + - Thu, 06 Feb 2025 05:53:46 GMT + Content-Type: + - application/json + Content-Length: + - '69' + Connection: + - keep-alive + Set-Cookie: + - AUTH_SESSION_ID=1738821227.68.76358.312212|78230f584c0d7db97d376e98de5321dc; + Path=/; Secure; HttpOnly + Cache-Control: + - no-store + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + body: + encoding: UTF-8 + string: '{"error":"invalid_grant","error_description":"Invalid refresh token"}' + recorded_at: Thu, 06 Feb 2025 05:53:46 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/system/admin/dfc_product_import_spec.rb b/spec/system/admin/dfc_product_import_spec.rb index bae9881278d..ca93801b862 100644 --- a/spec/system/admin/dfc_product_import_spec.rb +++ b/spec/system/admin/dfc_product_import_spec.rb @@ -100,4 +100,25 @@ click_button "Import" expect(page).to have_content "param is missing or the value is empty: catalog_url" end + + it "prompts to refresh OIDC connection", vcr: true do + # Stale access token will be renewed, but refresh token isn't valid either. + user.oidc_account.update!( + refresh_token: "something-expired-or-invalid", + updated_at: 1.day.ago, + ) + + catalog_url = "https://example.net/unauthorized" + stub_request(:get, catalog_url).to_return(status: [401, "Unauthorized"]) + + visit admin_product_import_path + + select enterprise.name, from: "Enterprise" + fill_in "catalog_url", with: catalog_url + + click_button "Import" + + expect(page).to have_content "OIDC Settings" + expect(page).to have_content "Connecting with your OIDC account failed." + end end From eeeded515ac8b26d6a15b38f76b3c3f49d919207 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 6 Feb 2025 17:03:08 +1100 Subject: [PATCH 3/5] Reduce complexity and outdated comments --- .../admin/dfc_product_imports_controller.rb | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/controllers/admin/dfc_product_imports_controller.rb b/app/controllers/admin/dfc_product_imports_controller.rb index 37adc70557e..2ddde56116b 100644 --- a/app/controllers/admin/dfc_product_imports_controller.rb +++ b/app/controllers/admin/dfc_product_imports_controller.rb @@ -11,19 +11,8 @@ def model_class end def index - # The plan: - # - # * Fetch DFC catalog as JSON from URL. - enterprise = OpenFoodNetwork::Permissions.new(spree_current_user) - .managed_product_enterprises.is_primary_producer - .find(params.require(:enterprise_id)) - - catalog_url = params.require(:catalog_url) - catalog = DfcCatalog.load(spree_current_user, catalog_url) catalog.apply_wholesale_values! - # * First step: import all products for given enterprise. - # * Second step: render table and let user decide which ones to import. imported = catalog.products.map do |subject| existing_variant = enterprise.supplied_variants.linked_to(subject.semanticId) @@ -44,5 +33,20 @@ def index flash[:error] = t(".connection_invalid") redirect_to admin_oidc_settings_path end + + private + + def enterprise + @enterprise ||= OpenFoodNetwork::Permissions.new(spree_current_user) + .managed_product_enterprises.is_primary_producer + .find(params.require(:enterprise_id)) + end + + def catalog + @catalog ||= DfcCatalog.load( + spree_current_user, + params.require(:catalog_url) + ) + end end end From 8b7b59d49e1306a4ef3a0eeec0596347f30571d6 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 6 Feb 2025 17:11:37 +1100 Subject: [PATCH 4/5] Link to OIDC settings from error message The colour of the link is really bad though. --- app/controllers/admin/dfc_product_imports_controller.rb | 8 ++++++-- config/locales/en.yml | 4 ++-- spec/system/admin/dfc_product_import_spec.rb | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/controllers/admin/dfc_product_imports_controller.rb b/app/controllers/admin/dfc_product_imports_controller.rb index 2ddde56116b..ee8a722a3d4 100644 --- a/app/controllers/admin/dfc_product_imports_controller.rb +++ b/app/controllers/admin/dfc_product_imports_controller.rb @@ -30,8 +30,12 @@ def index flash[:error] = e.message redirect_to admin_product_import_path rescue Rack::OAuth2::Client::Error - flash[:error] = t(".connection_invalid") - redirect_to admin_oidc_settings_path + oidc_settings_link = helpers.link_to( + t('spree.admin.tab.oidc_settings'), + admin_oidc_settings_path + ) + flash[:error] = t(".connection_invalid_html", oidc_settings_link:) + redirect_to admin_product_import_path end private diff --git a/config/locales/en.yml b/config/locales/en.yml index 36b8c93139b..42e76c869f8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -846,9 +846,9 @@ en: map: Map dfc_product_imports: - connection_invalid: | + connection_invalid_html: | Connecting with your OIDC account failed. - Please refresh your OIDC connection. + Please refresh your OIDC connection at: %{oidc_settings_link} index: title: "Importing a DFC product catalog" imported_products: "Imported products:" diff --git a/spec/system/admin/dfc_product_import_spec.rb b/spec/system/admin/dfc_product_import_spec.rb index ca93801b862..3f94bd7cff5 100644 --- a/spec/system/admin/dfc_product_import_spec.rb +++ b/spec/system/admin/dfc_product_import_spec.rb @@ -118,7 +118,8 @@ click_button "Import" - expect(page).to have_content "OIDC Settings" + expect(page).to have_content "Product Import" expect(page).to have_content "Connecting with your OIDC account failed." + expect(page).to have_content "Please refresh your OIDC connection at: OIDC Settings" end end From 327c2a5798ea2d69ab122bbd6d7a179b377b0900 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 7 Feb 2025 11:39:48 +1100 Subject: [PATCH 5/5] Style links within flash errors The default aqua link colour looked aweful on orange/red background. I tried a few different standard colours but couldn't get it right. So I reverted to the web standard link style: underline. That's looks pretty good. I personally think that we should use the more for links. --- app/webpacker/css/admin_v3/components/messages.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/webpacker/css/admin_v3/components/messages.scss b/app/webpacker/css/admin_v3/components/messages.scss index 0ec21b24765..7f02858ff3f 100644 --- a/app/webpacker/css/admin_v3/components/messages.scss +++ b/app/webpacker/css/admin_v3/components/messages.scss @@ -58,6 +58,11 @@ &.error { color: $color-flash-error-text; background-color: $color-flash-error-bg; + + a:not(.button) { + color: $color-flash-error-text; + text-decoration: underline; + } } .msg {