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) +}