diff --git a/app/serializers/tournament_serializer.rb b/app/serializers/tournament_serializer.rb index 26e817a..a01caf1 100644 --- a/app/serializers/tournament_serializer.rb +++ b/app/serializers/tournament_serializer.rb @@ -2,8 +2,10 @@ class TournamentSerializer < SimpleTournamentSerializer attributes :description, :playoff_teams_amount, - :instant_finalists_amount, :intermediate_round_participants_amount, - :read_only_mode, :sync_target_url, :sync_last_push_error + :instant_finalists_amount, :intermediate_round_participants_amount + attribute :read_only_mode, if: :sync_metadata_visible? + attribute :sync_target_url, if: :sync_metadata_visible? + attribute :sync_last_push_error, if: :sync_metadata_visible? # NEVER expose sync_auth_token anywhere - it should only ever be written to or checked against has_many :stages @@ -12,7 +14,7 @@ class TournamentSerializer < SimpleTournamentSerializer object.timer_end&.iso8601 end - attribute :sync_last_pushed_at do + attribute :sync_last_pushed_at, if: :sync_metadata_visible? do object.sync_last_pushed_at&.iso8601 end @@ -30,4 +32,8 @@ class TournamentSerializer < SimpleTournamentSerializer } end end + + def sync_metadata_visible? + scope.present? && scope == object.owner + end end diff --git a/spec/controllers/tournaments_controller_spec.rb b/spec/controllers/tournaments_controller_spec.rb index 9bc9c39..4f53e0a 100644 --- a/spec/controllers/tournaments_controller_spec.rb +++ b/spec/controllers/tournaments_controller_spec.rb @@ -103,21 +103,21 @@ RSpec.describe TournamentsController, type: :controller do json = deserialize_response(response) expect(json[:id].to_i).to eq(@tournament.id) expected_keys = %i[ - id name code public description playoff_teams_amount instant_finalists_amount - intermediate_round_participants_amount read_only_mode sync_target_url - sync_last_push_error sync_last_pushed_at timer_end owner_username stages teams + id name code public description playoff_teams_amount + instant_finalists_amount intermediate_round_participants_amount + timer_end owner_username stages teams ] expect(json.keys).to match_array(expected_keys) - expect(json).to eq(TournamentSerializer.new(@tournament).as_json) expect(json[:name]).to eq(@tournament.name) expect(json[:description]).to eq(@tournament.description) expect(json[:public]).to eq(@tournament.public) end - it 'returns sync metadata on full tournament payload' do + it 'does not return sync metadata on unauthenticated requests' do pushed_at = Time.utc(2026, 4, 20, 12, 0, 0) @tournament.update!( read_only_mode: true, + sync_target_url: 'https://remote.example.com/tournaments/1/sync_state', sync_auth_token: 'shared-secret', sync_last_pushed_at: pushed_at, sync_last_push_error: 'push failed' @@ -125,11 +125,34 @@ RSpec.describe TournamentsController, type: :controller do get :show, params: { id: @tournament.to_param } + json = deserialize_response(response) + expect(json).not_to have_key(:read_only_mode) + expect(json).not_to have_key(:sync_target_url) + expect(json).not_to have_key(:sync_last_pushed_at) + expect(json).not_to have_key(:sync_last_push_error) + expect(json).not_to have_key(:sync_auth_token) + end + + it 'returns sync metadata to owner requests' do + pushed_at = Time.utc(2026, 4, 20, 12, 0, 0) + @tournament.update!( + read_only_mode: true, + sync_target_url: 'https://remote.example.com/tournaments/1/sync_state', + sync_auth_token: 'shared-secret', + sync_last_pushed_at: pushed_at, + sync_last_push_error: 'push failed' + ) + apply_authentication_headers_for @tournament.owner + + get :show, params: { id: @tournament.to_param } + json = deserialize_response(response) expect(json[:read_only_mode]).to eq(true) + expect(json[:sync_target_url]).to eq('https://remote.example.com/tournaments/1/sync_state') expect(json[:sync_last_pushed_at]).to eq(pushed_at.iso8601) expect(json[:sync_last_push_error]).to eq('push failed') expect(json).not_to have_key(:sync_auth_token) + expect(json).to eq(TournamentSerializer.new(@tournament, scope: @tournament.owner).as_json) end context 'with simple=true parameter' do