From ec3848d048474f6f976a320824846d20f1b3bbb7 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:19:18 -0700 Subject: [PATCH] Allow access authorities to view unlisted tracks The is_unlisted filter was blocking tracks even when the requesting wallet matched an access authority. Add the access authority check to the unlisted condition so gated unlisted tracks are visible to their access authorities. Co-Authored-By: Claude Opus 4.6 --- api/dbv1/get_tracks.sql.go | 15 +++++++++------ api/dbv1/queries/get_tracks.sql | 5 ++++- api/v1_tracks_test.go | 26 ++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/api/dbv1/get_tracks.sql.go b/api/dbv1/get_tracks.sql.go index f5ee3142..42dd4770 100644 --- a/api/dbv1/get_tracks.sql.go +++ b/api/dbv1/get_tracks.sql.go @@ -233,19 +233,22 @@ FROM tracks t JOIN aggregate_track using (track_id) LEFT JOIN aggregate_plays on play_item_id = t.track_id LEFT JOIN track_routes on t.track_id = track_routes.track_id and track_routes.is_current = true -WHERE (is_unlisted = false OR t.owner_id = $1 OR $2::bool = TRUE) - AND t.track_id = ANY($3::int[]) +WHERE (is_unlisted = false OR t.owner_id = $1 OR $2::bool = TRUE + OR (COALESCE($3, '') <> '' + AND t.access_authorities IS NOT NULL + AND EXISTS (SELECT 1 FROM unnest(t.access_authorities) aa WHERE lower(aa) = lower($3)))) + AND t.track_id = ANY($4::int[]) AND (t.access_authorities IS NULL - OR (COALESCE($4, '') <> '' - AND EXISTS (SELECT 1 FROM unnest(t.access_authorities) aa WHERE lower(aa) = lower($4)))) + OR (COALESCE($3, '') <> '' + AND EXISTS (SELECT 1 FROM unnest(t.access_authorities) aa WHERE lower(aa) = lower($3)))) ORDER BY t.track_id ` type GetTracksParams struct { MyID interface{} `json:"my_id"` IncludeUnlisted bool `json:"include_unlisted"` - Ids []int32 `json:"ids"` AuthedWallet interface{} `json:"authed_wallet"` + Ids []int32 `json:"ids"` } type GetTracksRow struct { @@ -329,8 +332,8 @@ func (q *Queries) GetTracks(ctx context.Context, arg GetTracksParams) ([]GetTrac rows, err := q.db.Query(ctx, getTracks, arg.MyID, arg.IncludeUnlisted, - arg.Ids, arg.AuthedWallet, + arg.Ids, ) if err != nil { return nil, err diff --git a/api/dbv1/queries/get_tracks.sql b/api/dbv1/queries/get_tracks.sql index 4702daaa..62948269 100644 --- a/api/dbv1/queries/get_tracks.sql +++ b/api/dbv1/queries/get_tracks.sql @@ -218,7 +218,10 @@ FROM tracks t JOIN aggregate_track using (track_id) LEFT JOIN aggregate_plays on play_item_id = t.track_id LEFT JOIN track_routes on t.track_id = track_routes.track_id and track_routes.is_current = true -WHERE (is_unlisted = false OR t.owner_id = @my_id OR @include_unlisted::bool = TRUE) +WHERE (is_unlisted = false OR t.owner_id = @my_id OR @include_unlisted::bool = TRUE + OR (COALESCE(@authed_wallet, '') <> '' + AND t.access_authorities IS NOT NULL + AND EXISTS (SELECT 1 FROM unnest(t.access_authorities) aa WHERE lower(aa) = lower(@authed_wallet)))) AND t.track_id = ANY(@ids::int[]) AND (t.access_authorities IS NULL OR (COALESCE(@authed_wallet, '') <> '' diff --git a/api/v1_tracks_test.go b/api/v1_tracks_test.go index 8506da73..d3f13bf8 100644 --- a/api/v1_tracks_test.go +++ b/api/v1_tracks_test.go @@ -62,3 +62,29 @@ func TestGetTracksExcludesAccessAuthorities(t *testing.T) { assert.Equal(t, "T1", resp.Data[0].Title.String) assert.Equal(t, []string{gateWallet}, resp.Data[0].AccessAuthorities) } + +func TestGetUnlistedTrackWithAccessAuthority(t *testing.T) { + app := testAppWithFixtures(t) + ctx := context.Background() + require.NotNil(t, app.writePool, "test requires write pool") + + gateWallet := "0x7d273271690538cf855e5b3002a0dd8c154bb060" + // Make track 100 both unlisted and gated by access_authorities + _, err := app.writePool.Exec(ctx, `UPDATE tracks SET is_unlisted = true, access_authorities = ARRAY[$1]::text[] WHERE track_id = 100 AND is_current = true`, gateWallet) + require.NoError(t, err) + + var resp struct { + Data []dbv1.Track + } + + // Without auth: unlisted + gated track must not be returned + status, _ := testGet(t, app, "/v1/full/tracks?id=eYZmn", &resp) + assert.Equal(t, 200, status) + assert.Len(t, resp.Data, 0, "unlisted track with access_authorities must not be returned when unauthenticated") + + // With auth signed by access authority: unlisted track must be returned + status, _ = testGetWithWallet(t, app, "/v1/full/tracks?id=eYZmn", gateWallet, &resp) + assert.Equal(t, 200, status) + assert.Len(t, resp.Data, 1, "unlisted track with access_authorities must be returned when request is signed by matching authority") + assert.Equal(t, "T1", resp.Data[0].Title.String) +}