From b26fec292a6b7757e7eaf67dc0bf588ef25f0871 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 14 Sep 2026 12:42:23 +0000 Subject: [PATCH 1/2] Ignore undecryptable icalendar auth tokens ActivityParticipations#index 500s when Organization#icalendar_auth_token cannot be decrypted, because the calendar sidebar presence check reads the encrypted attribute. Treat decryption failures as a missing token so that page still renders. Co-authored-by: Thibaud Guillaume-Gentil --- app/models/organization.rb | 6 +++ ...participations_calendar_controller_test.rb | 6 +++ ...activity_participations_controller_test.rb | 38 +++++++++++++++++++ test/models/organization_test.rb | 9 +++++ 4 files changed, 59 insertions(+) create mode 100644 test/controllers/activity_participations_controller_test.rb diff --git a/app/models/organization.rb b/app/models/organization.rb index fc5c74024..09d1ec53a 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -134,6 +134,12 @@ def self.instance first! end + def icalendar_auth_token + super + rescue ActiveRecord::Encryption::Errors::Decryption + nil + end + def features self[:features].map(&:to_sym) & FEATURES end diff --git a/test/controllers/activity_participations_calendar_controller_test.rb b/test/controllers/activity_participations_calendar_controller_test.rb index 80e25cae0..94987c0c6 100644 --- a/test/controllers/activity_participations_calendar_controller_test.rb +++ b/test/controllers/activity_participations_calendar_controller_test.rb @@ -15,6 +15,12 @@ def request(auth_token: nil) assert_response :unauthorized end + test "with an undecryptable auth token" do + Current.org.update_column(:icalendar_auth_token, "invalid-ciphertext") + request(auth_token: "1234abcd") + assert_response :unauthorized + end + test "with a wrong auth token" do request(auth_token: "wrong") assert_response :unauthorized diff --git a/test/controllers/activity_participations_controller_test.rb b/test/controllers/activity_participations_controller_test.rb new file mode 100644 index 000000000..2fadaf2cc --- /dev/null +++ b/test/controllers/activity_participations_controller_test.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "test_helper" + +class ActivityParticipationsControllerTest < ActionDispatch::IntegrationTest + setup do + host! "admin.acme.test" + end + + def login(admin) + session = Session.create!( + admin_email: admin.email, + remote_addr: "127.0.0.1", + user_agent: "Test Browser") + get "/sessions/#{session.generate_token_for(:redeem)}" + end + + test "index shows calendar subscribe link when icalendar_auth_token is present" do + travel_to "2024-01-01" + login admins(:super) + + get activity_participations_path + + assert_response :success + assert_match(/webcal/, response.body) + end + + test "index succeeds when icalendar_auth_token cannot be decrypted" do + travel_to "2024-01-01" + login admins(:super) + Current.org.update_column(:icalendar_auth_token, "invalid-ciphertext") + + get activity_participations_path + + assert_response :success + assert_no_match(/webcal/, response.body) + end +end diff --git a/test/models/organization_test.rb b/test/models/organization_test.rb index 2c19bc448..9c1c05d93 100644 --- a/test/models/organization_test.rb +++ b/test/models/organization_test.rb @@ -392,4 +392,13 @@ class OrganizationTest < ActiveSupport::TestCase assert Current.org.encrypted_attribute?(:api_token) assert Current.org.encrypted_attribute?(:icalendar_auth_token) end + + test "icalendar_auth_token presence is false when ciphertext cannot be decrypted" do + org = Current.org + org.update_column(:icalendar_auth_token, "invalid-ciphertext") + org.reload + + assert_nil org.icalendar_auth_token + assert_not org.icalendar_auth_token? + end end From 11065a72352026fef62cd92b86969b695a85c1e0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 14 Sep 2026 12:50:12 +0000 Subject: [PATCH 2/2] Test undecryptable icalendar tokens with raw ciphertext Write a corrupted encrypted payload via SQL so Active Record does not re-encrypt it. That reproduces the production Decryption error on read. Co-authored-by: Thibaud Guillaume-Gentil --- ...tivity_participations_calendar_controller_test.rb | 2 +- .../activity_participations_controller_test.rb | 6 +++--- test/models/organization_test.rb | 9 ++++----- test/support/organizations_helper.rb | 12 ++++++++++++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/test/controllers/activity_participations_calendar_controller_test.rb b/test/controllers/activity_participations_calendar_controller_test.rb index 94987c0c6..068c5703e 100644 --- a/test/controllers/activity_participations_calendar_controller_test.rb +++ b/test/controllers/activity_participations_calendar_controller_test.rb @@ -16,7 +16,7 @@ def request(auth_token: nil) end test "with an undecryptable auth token" do - Current.org.update_column(:icalendar_auth_token, "invalid-ciphertext") + corrupt_icalendar_auth_token! request(auth_token: "1234abcd") assert_response :unauthorized end diff --git a/test/controllers/activity_participations_controller_test.rb b/test/controllers/activity_participations_controller_test.rb index 2fadaf2cc..aeda3c153 100644 --- a/test/controllers/activity_participations_controller_test.rb +++ b/test/controllers/activity_participations_controller_test.rb @@ -22,17 +22,17 @@ def login(admin) get activity_participations_path assert_response :success - assert_match(/webcal/, response.body) + assert_select "#calendar_sidebar_section" end test "index succeeds when icalendar_auth_token cannot be decrypted" do travel_to "2024-01-01" login admins(:super) - Current.org.update_column(:icalendar_auth_token, "invalid-ciphertext") + corrupt_icalendar_auth_token! get activity_participations_path assert_response :success - assert_no_match(/webcal/, response.body) + assert_select "#calendar_sidebar_section", false end end diff --git a/test/models/organization_test.rb b/test/models/organization_test.rb index 9c1c05d93..4cfb286be 100644 --- a/test/models/organization_test.rb +++ b/test/models/organization_test.rb @@ -394,11 +394,10 @@ class OrganizationTest < ActiveSupport::TestCase end test "icalendar_auth_token presence is false when ciphertext cannot be decrypted" do - org = Current.org - org.update_column(:icalendar_auth_token, "invalid-ciphertext") - org.reload + corrupt_icalendar_auth_token! + organization = Organization.uncached { Organization.find(Current.org.id) } - assert_nil org.icalendar_auth_token - assert_not org.icalendar_auth_token? + assert_nil organization.icalendar_auth_token + assert_not organization.icalendar_auth_token? end end diff --git a/test/support/organizations_helper.rb b/test/support/organizations_helper.rb index 8d9ea3cfd..5f7af9607 100644 --- a/test/support/organizations_helper.rb +++ b/test/support/organizations_helper.rb @@ -5,6 +5,18 @@ def org(columns = {}) Current.org.update_columns(columns) end + def corrupt_icalendar_auth_token!(organization = Current.org) + ciphertext = JSON.parse(organization.ciphertext_for(:icalendar_auth_token)) + ciphertext["p"] = ciphertext.fetch("p").tr("A-Za-z0-9", "B-ZAb-z0-91") + # Raw SQL: update_column would re-encrypt this payload as plaintext. + Organization.connection.update( + Organization.sanitize_sql_array([ + "UPDATE organizations SET icalendar_auth_token = ? WHERE id = ?", + ciphertext.to_json, + organization.id + ])) + end + def german_org(columns = {}) attrs = { languages: [ "de" ],