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..068c5703e 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 + corrupt_icalendar_auth_token! + 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..aeda3c153 --- /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_select "#calendar_sidebar_section" + end + + test "index succeeds when icalendar_auth_token cannot be decrypted" do + travel_to "2024-01-01" + login admins(:super) + corrupt_icalendar_auth_token! + + get activity_participations_path + + assert_response :success + assert_select "#calendar_sidebar_section", false + end +end diff --git a/test/models/organization_test.rb b/test/models/organization_test.rb index 2c19bc448..4cfb286be 100644 --- a/test/models/organization_test.rb +++ b/test/models/organization_test.rb @@ -392,4 +392,12 @@ 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 + corrupt_icalendar_auth_token! + organization = Organization.uncached { Organization.find(Current.org.id) } + + 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" ],