Skip to content
Draft
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
8 changes: 8 additions & 0 deletions app/controllers/tracks_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class TracksController < ApplicationController
include ActionController::Live

before_action :authenticate_from_play_token, only: %i[audio]
before_action :set_track, only: %i[show update destroy audio merge]

has_scope :by_filter, as: 'filter'
Expand Down Expand Up @@ -86,6 +87,13 @@ def merge

private

def authenticate_from_play_token
return if current_user.present?

token = AuthToken.find_by(play_token: params[:play_token])
self.current_user = token&.user
end

def audio_with_file(path, mimetype)
file = File.open(path, 'rb')
audio_with_stream(file, mimetype, file.size)
Expand Down
10 changes: 10 additions & 0 deletions app/models/auth_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# id :bigint not null, primary key
# hashed_secret :string not null
# play_token :string not null
# user_agent :string not null
# device_id :string not null
# user_id :bigint not null
Expand All @@ -14,9 +15,11 @@ class AuthToken < ApplicationRecord

validates :device_id, presence: true, uniqueness: true
validates :hashed_secret, presence: true
validates :play_token, presence: true
validates :user_agent, presence: true
before_validation :generate_device_id, unless: :device_id?
before_validation :generate_secret, unless: :hashed_secret?
before_validation :generate_play_token, unless: :play_token?

attr_accessor :secret

Expand All @@ -42,4 +45,11 @@ def generate_secret
self.secret = SecureRandom.urlsafe_base64(48)
self.hashed_secret = BCrypt::Password.create(secret, cost: Rails.configuration.token_hash_rounds)
end

def generate_play_token
loop do
self.play_token = SecureRandom.urlsafe_base64(48)
break unless AuthToken.exists?(play_token: play_token)
end
end
end
3 changes: 2 additions & 1 deletion app/serializers/auth_token_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
#
# id :bigint not null, primary key
# hashed_secret :string not null
# play_token :string not null
# user_agent :string not null
# device_id :string not null
# user_id :bigint not null
#
class AuthTokenSerializer < ActiveModel::Serializer
attributes :id, :device_id, :user_id, :user_agent
attributes :id, :device_id, :user_id, :user_agent, :play_token
end
19 changes: 19 additions & 0 deletions db/migrate/20211228193422_add_play_token_to_auth_token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class AddPlayTokenToAuthToken < ActiveRecord::Migration[6.1]

def up
change_table :auth_tokens do |t|
t.string :play_token, null: true
t.index :play_token
end
AuthToken.find_each do |token|
token.validate # will trigger generate_play_token
token.save
end
change_column :auth_tokens, :play_token, :string, null: false
end

def down
remove_index :auth_tokens, :play_token
remove_column :auth_tokens, :play_token
end
end
4 changes: 3 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2021_11_27_114919) do
ActiveRecord::Schema.define(version: 2021_12_28_193422) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -108,7 +108,9 @@
t.string "device_id", null: false
t.string "hashed_secret", null: false
t.string "user_agent", null: false
t.string "play_token", null: false
t.index ["device_id"], name: "index_auth_tokens_on_device_id", unique: true
t.index ["play_token"], name: "index_auth_tokens_on_play_token"
t.index ["user_id"], name: "index_auth_tokens_on_user_id"
end

Expand Down
25 changes: 25 additions & 0 deletions test/controllers/tracks_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,31 @@ class TracksControllerTest < ActionDispatch::IntegrationTest
end
end

class TracksControllerPlayTokenTest < ActionDispatch::IntegrationTest
test 'should not serve audio to user without tokens' do
location = Location.create(path: Rails.root.join('test/files'))
audio_file = create(:audio_file, location: location, filename: '/base.flac')
track = create(:track, audio_file: audio_file)

get audio_track_url(track)

assert_response :unauthorized
end

test 'should serve audio to user with play token' do
token = create(:auth_token)
user = token.user

location = Location.create(path: Rails.root.join('test/files'))
audio_file = create(:audio_file, location: location, filename: '/base.flac')
track = create(:track, audio_file: audio_file)

get audio_track_url(track, play_token: token.play_token)

assert_response :success
end
end

class TracksControllerAudioTest < ActionDispatch::IntegrationTest
setup do
sign_in_as(create(:user))
Expand Down
1 change: 1 addition & 0 deletions test/factories/auth_tokens.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# id :bigint not null, primary key
# hashed_secret :string not null
# play_token :string not null
# user_agent :string not null
# device_id :string not null
# user_id :bigint not null
Expand Down
1 change: 1 addition & 0 deletions test/models/auth_token_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# id :bigint not null, primary key
# hashed_secret :string not null
# play_token :string not null
# user_agent :string not null
# device_id :string not null
# user_id :bigint not null
Expand Down