Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/comms/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,8 @@ func getNewBlasts(tx dbv1.DBTX, ctx context.Context, arg getNewBlastsParams) ([]
WHERE blast.audience = 'remixer_audience'
AND og.owner_id = blast.from_user_id
AND t.owner_id = @user_id
AND t.access_authorities IS NULL
AND og.access_authorities IS NULL
AND (
blast.audience_content_id IS NULL
OR (
Expand Down
2 changes: 2 additions & 0 deletions api/comms/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,8 @@ func hasNewBlastFromUser(pool *dbv1.DBPools, ctx context.Context, userID int32,
JOIN tracks og ON remixes.parent_track_id = og.track_id
WHERE og.owner_id = blast.from_user_id
AND t.owner_id = $1
AND t.access_authorities IS NULL
AND og.access_authorities IS NULL
AND (
blast.audience_content_id IS NULL
OR (
Expand Down
2 changes: 2 additions & 0 deletions api/comms_blasts.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ func (app *ApiServer) getNewBlasts(c *fiber.Ctx) error {
WHERE blast.audience = 'remixer_audience'
AND og.owner_id = blast.from_user_id
AND t.owner_id = @user_id
AND t.access_authorities IS NULL
AND og.access_authorities IS NULL
AND (
blast.audience_content_id IS NULL
OR (
Expand Down
1 change: 1 addition & 0 deletions api/dbv1/get_events.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/dbv1/get_genres.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/dbv1/get_track_ids_by_isrc.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/dbv1/get_tracks.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions api/dbv1/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/dbv1/queries/get_events.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ SELECT
e.event_data AS event_data
FROM events e
LEFT JOIN tracks t ON t.track_id = e.entity_id AND t.is_current = true AND e.entity_type = 'track'
AND t.access_authorities IS NULL
WHERE
(@entity_ids::int[] = '{}' OR e.entity_id = ANY(@entity_ids::int[]))
AND (@event_ids::int[] = '{}' OR e.event_id = ANY(@event_ids::int[]))
Expand Down
1 change: 1 addition & 0 deletions api/dbv1/queries/get_genres.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ WHERE
AND genre != ''
AND is_current = TRUE
AND created_at > @start_time
AND access_authorities IS NULL
GROUP BY
genre
ORDER BY
Expand Down
3 changes: 2 additions & 1 deletion api/dbv1/queries/get_track_ids_by_isrc.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
-- name: GetTrackIdsByISRC :many
SELECT track_id
FROM tracks
WHERE isrc = ANY(@isrcs::text[]);
WHERE isrc = ANY(@isrcs::text[])
AND access_authorities IS NULL;
1 change: 1 addition & 0 deletions api/dbv1/queries/get_tracks.sql
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,6 @@ 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)
AND t.track_id = ANY(@ids::int[])
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetTracks filters out gated tracks with t.access_authorities IS NULL, but it doesn’t filter t.is_current = true. Since tracks is versioned (PK includes txhash and has is_current), a track whose current row is gated could still be returned via an older is_current=false row where access_authorities is NULL. Add AND t.is_current = true (and ensure joins/selects are consistent) so the exclusion can’t be bypassed via historical rows.

Suggested change
AND t.track_id = ANY(@ids::int[])
AND t.track_id = ANY(@ids::int[])
AND t.is_current = true

Copilot uses AI. Check for mistakes.
AND t.access_authorities IS NULL
ORDER BY t.track_id
;
24 changes: 24 additions & 0 deletions api/v1_events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,27 @@ func TestGetEventsExcludesDeletedTracks(t *testing.T) {
"data.3.entity_id": trashid.MustEncodeHashID(101),
})
}

func TestGetEventsExcludesAccessAuthoritiesTracks(t *testing.T) {
app := testAppWithFixtures(t)
ctx := context.Background()
require.NotNil(t, app.writePool, "test requires write pool")

// Set access_authorities on track 102 (which has event 6) so it is gated
_, err := app.writePool.Exec(ctx, `UPDATE tracks SET access_authorities = ARRAY['0x123']::text[] WHERE track_id = 102 AND is_current = true`)
require.NoError(t, err)

var eventsResponse struct {
Data []dbv1.FullEvent
}
status, _ := testGet(t, app, "/v1/events", &eventsResponse)
assert.Equal(t, 200, status)

// Event 6 is for entity_id 102; it must not appear when the track has access_authorities
entity102Hash := trashid.MustEncodeHashID(102)
for _, e := range eventsResponse.Data {
assert.NotEqual(t, entity102Hash, e.EntityId, "events for access_authorities track 102 must not be returned")
}

assert.Len(t, eventsResponse.Data, 4, "expected 4 events after excluding event for access_authorities track")
}
51 changes: 51 additions & 0 deletions api/v1_metrics_genres_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package api

import (
"context"
"fmt"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestMetricsGenres(t *testing.T) {
Expand Down Expand Up @@ -41,3 +43,52 @@ func TestMetricsGenres(t *testing.T) {
}

}

func TestMetricsGenresExcludesAccessAuthoritiesTracks(t *testing.T) {
app := testAppWithFixtures(t)
ctx := context.Background()
require.NotNil(t, app.writePool, "test requires write pool")

// Get baseline Electronic count (use epoch so all fixture tracks are included)
url := fmt.Sprintf("/v1/metrics/genres?start_time=%d", 0)
var before struct {
Data []struct {
Name string `json:"name"`
Count int64 `json:"count"`
}
Comment on lines +54 to +58
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test unmarshals name, but TestMetricsGenres in the same file unmarshals genre and can silently pass without asserting anything. Update the older test to use the same response shape (name) and add a real assertion (e.g., expect at least one known genre) so the baseline coverage is meaningful.

Copilot uses AI. Check for mistakes.
}
status, _ := testGet(t, app, url, &before)
require.Equal(t, 200, status)

var electronicCountBefore int64
for _, g := range before.Data {
if g.Name == "Electronic" {
electronicCountBefore = g.Count
break
}
}
require.Greater(t, electronicCountBefore, int64(0), "fixtures should have Electronic tracks")

// Gate one Electronic track (track 100 is Electronic)
_, err := app.writePool.Exec(ctx, `UPDATE tracks SET access_authorities = ARRAY['0xgate']::text[] WHERE track_id = 100 AND is_current = true`)
require.NoError(t, err)

var after struct {
Data []struct {
Name string `json:"name"`
Count int64 `json:"count"`
}
}
status, _ = testGet(t, app, url, &after)
require.Equal(t, 200, status)

var electronicCountAfter int64
for _, g := range after.Data {
if g.Name == "Electronic" {
electronicCountAfter = g.Count
break
}
}
assert.Equal(t, electronicCountBefore-1, electronicCountAfter, "genre count must exclude access_authorities tracks")
}

2 changes: 2 additions & 0 deletions api/v1_playlists_trending.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ func (app *ApiServer) v1PlaylistsTrending(c *fiber.Ctx) error {
pt.is_removed = false
AND t.is_delete = false
AND t.is_current = true
AND t.stem_of IS NULL
AND t.access_authorities IS NULL
)
SELECT
playlist_id
Expand Down
10 changes: 10 additions & 0 deletions api/v1_resolve_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package api

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestResolveTrackURL(t *testing.T) {
Expand All @@ -12,6 +14,14 @@ func TestResolveTrackURL(t *testing.T) {
status, _ := testGet(t, app, "/v1/resolve?url=https://audius.co/TracksByPermalink/track-by-permalink")
assert.Equal(t, 302, status)

// Resolve does not filter by access_authorities (no tracks join); filter happens in GetTracks.
// So a gated track still resolves (302), but fetching the track by ID returns empty.
require.NotNil(t, app.writePool, "test requires write pool")
_, err := app.writePool.Exec(context.Background(), `UPDATE tracks SET access_authorities = ARRAY['0xabc']::text[] WHERE track_id = 500 AND is_current = true`)
require.NoError(t, err)
status, _ = testGet(t, app, "/v1/resolve?url=https://audius.co/TracksByPermalink/track-by-permalink")
assert.Equal(t, 302, status, "resolve still redirects; GetTracks filters gated tracks when fetching by ID")

// Test failed track resolution
status, _ = testGet(t, app, "/v1/resolve?url=https://audius.co/nonexistent/track")
assert.Equal(t, 404, status)
Expand Down
2 changes: 1 addition & 1 deletion api/v1_track_comment_count.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func (app *ApiServer) v1TrackCommentCount(c *fiber.Ctx) error {
track AS (
SELECT track_id, owner_id
FROM tracks
WHERE track_id = @trackId
WHERE track_id = @trackId AND access_authorities IS NULL
),

-- Users muted by high-karma users
Expand Down
2 changes: 1 addition & 1 deletion api/v1_track_comments.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (app *ApiServer) v1TrackComments(c *fiber.Ctx) error {
track AS (
SELECT track_id, owner_id
FROM tracks
WHERE track_id = @track_id
WHERE track_id = @track_id AND access_authorities IS NULL
),

-- Users muted by high-karma users
Expand Down
1 change: 1 addition & 0 deletions api/v1_tracks_feeling_lucky.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func (app *ApiServer) v1TracksFeelingLucky(c *fiber.Ctx) error {
"is_delete = false",
"is_unlisted = false",
"stem_of IS NULL",
"access_authorities IS NULL",
"aggregate_plays.count >= 250",
}
if params.MinFollowers != 0 {
Expand Down
2 changes: 2 additions & 0 deletions api/v1_tracks_recent_premium.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func (app *ApiServer) v1TracksRecentPremium(c *fiber.Ctx) error {
is_unlisted = false AND
is_available = true AND
is_delete = false AND
stem_of IS NULL AND
access_authorities IS NULL AND
(stream_conditions ? 'usdc_purchase' OR download_conditions ? 'usdc_purchase') AND
created_at >= now() - interval '1 month'
ORDER BY owner_id, created_at DESC
Expand Down
19 changes: 19 additions & 0 deletions api/v1_tracks_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package api

import (
"context"
"testing"

"api.audius.co/api/dbv1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestTracksEndpoint(t *testing.T) {
Expand Down Expand Up @@ -33,3 +35,20 @@ func TestGetTracksByPermalink(t *testing.T) {
"data.0.title": "track by permalink",
})
}

func TestGetTracksExcludesAccessAuthorities(t *testing.T) {
app := testAppWithFixtures(t)
ctx := context.Background()
require.NotNil(t, app.writePool, "test requires write pool")

// Track 100 has title "T1" and is returned as id eYZmn. Set access_authorities so it is gated.
_, err := app.writePool.Exec(ctx, `UPDATE tracks SET access_authorities = ARRAY['0xgate']::text[] WHERE track_id = 100 AND is_current = true`)
require.NoError(t, err)

var resp struct {
Data []dbv1.Track
}
status, _ := testGet(t, app, "/v1/full/tracks?id=eYZmn", &resp)
assert.Equal(t, 200, status)
assert.Len(t, resp.Data, 0, "tracks with access_authorities must not be returned")
}
1 change: 1 addition & 0 deletions api/v1_users_feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func (app *ApiServer) v1UsersFeed(c *fiber.Ctx) error {
AND is_unlisted = false
AND is_delete = false
AND stem_of is null
AND access_authorities IS NULL
)

UNION ALL
Expand Down
2 changes: 1 addition & 1 deletion api/v1_users_listen_counts_monthly.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (app *ApiServer) v1UsersListenCountsMonthly(c *fiber.Ctx) error {
SUM(count) AS count
FROM aggregate_monthly_plays
WHERE play_item_id IN (
SELECT track_id from tracks where owner_id = @userId
SELECT track_id FROM tracks WHERE owner_id = @userId AND stem_of IS NULL AND access_authorities IS NULL
)
AND timestamp >= @startTime
AND timestamp < @endTime
Expand Down
2 changes: 2 additions & 0 deletions api/v1_users_recommended_tracks.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func (app *ApiServer) v1UsersRecommendedTracks(c *fiber.Ctx) error {
t.is_unlisted = false
AND t.is_current = true
AND t.is_delete = false
AND t.stem_of IS NULL
AND t.access_authorities IS NULL
AND u.is_deactivated = false
ORDER BY random()
LIMIT 10
Expand Down
2 changes: 2 additions & 0 deletions api/v1_users_related.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func (app *ApiServer) v1UsersRelated(c *fiber.Ctx) error {
AND t.is_unlisted IS false
AND t.is_available IS true
AND t.stem_of IS NULL
AND t.access_authorities IS NULL
AND owner_id = @userId
GROUP BY genre
ORDER BY count(*) DESC
Expand Down Expand Up @@ -101,6 +102,7 @@ func (app *ApiServer) v1UsersRelated(c *fiber.Ctx) error {
AND is_unlisted = false
AND is_available = true
AND stem_of IS NULL
AND access_authorities IS NULL
AND genre IS NOT NULL
GROUP BY genre
ORDER BY COUNT(*) DESC
Expand Down
1 change: 1 addition & 0 deletions api/v1_users_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func (app *ApiServer) v1UsersTags(c *fiber.Ctx) error {
AND is_unlisted = false
AND is_delete = false
AND stem_of is null
AND access_authorities IS NULL
) AS split_tags
WHERE tag != ''
GROUP BY tag
Expand Down
3 changes: 2 additions & 1 deletion api/v1_users_tracks.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ func (app *ApiServer) v1UserTracks(c *fiber.Ctx) error {
AND t.is_delete = false
AND t.is_available = true
AND ` + trackFilter + `
AND t.stem_of is null` + gateFilter + `
AND t.stem_of is null
AND t.access_authorities IS NULL` + gateFilter + `
ORDER BY (CASE WHEN t.track_id = u.artist_pick_track_id THEN 0 ELSE 1 END), ` + orderClause + `
LIMIT @limit
OFFSET @offset
Expand Down
1 change: 1 addition & 0 deletions api/v1_users_tracks_ai_attributed.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (app *ApiServer) v1UserTracksAiAttributed(c *fiber.Ctx) error {
AND t.is_available = true
AND ` + trackFilter + `
AND t.stem_of is null
AND t.access_authorities IS NULL
ORDER BY ` + orderClause + `
LIMIT @limit
OFFSET @offset
Expand Down
3 changes: 2 additions & 1 deletion api/v1_users_tracks_count.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func (app *ApiServer) v1UserTracksCount(c *fiber.Ctx) error {
AND t.is_delete = false
AND t.is_available = true
AND ` + trackFilter + `
AND t.stem_of is null` + gateFilter
AND t.stem_of is null
AND t.access_authorities IS NULL` + gateFilter

args := pgx.NamedArgs{
"user_id": userId,
Expand Down
Loading