From 2a5d0537abb5206d0365376a032dae78d2a31436 Mon Sep 17 00:00:00 2001 From: Rien Maertens Date: Sat, 1 Jan 2022 18:10:20 +0100 Subject: [PATCH 1/2] Add play_token field to AuthToken --- app/models/auth_token.rb | 10 ++++++++++ app/serializers/auth_token_serializer.rb | 1 + ...1228193422_add_play_token_to_auth_token.rb | 19 +++++++++++++++++++ db/schema.rb | 4 +++- test/factories/auth_tokens.rb | 1 + test/models/auth_token_test.rb | 1 + 6 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20211228193422_add_play_token_to_auth_token.rb diff --git a/app/models/auth_token.rb b/app/models/auth_token.rb index 69b87f60..ea1923f1 100644 --- a/app/models/auth_token.rb +++ b/app/models/auth_token.rb @@ -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 @@ -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 @@ -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 diff --git a/app/serializers/auth_token_serializer.rb b/app/serializers/auth_token_serializer.rb index a46c5a6c..909570ca 100644 --- a/app/serializers/auth_token_serializer.rb +++ b/app/serializers/auth_token_serializer.rb @@ -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 diff --git a/db/migrate/20211228193422_add_play_token_to_auth_token.rb b/db/migrate/20211228193422_add_play_token_to_auth_token.rb new file mode 100644 index 00000000..83885ae1 --- /dev/null +++ b/db/migrate/20211228193422_add_play_token_to_auth_token.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index b2e64617..d638c1aa 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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" @@ -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 diff --git a/test/factories/auth_tokens.rb b/test/factories/auth_tokens.rb index a3ceefc3..451e7dfc 100644 --- a/test/factories/auth_tokens.rb +++ b/test/factories/auth_tokens.rb @@ -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 diff --git a/test/models/auth_token_test.rb b/test/models/auth_token_test.rb index 94f713d2..88b0142a 100644 --- a/test/models/auth_token_test.rb +++ b/test/models/auth_token_test.rb @@ -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 From 89e374704ca10d1e44c35e6b7fb5571d46eb7670 Mon Sep 17 00:00:00 2001 From: Rien Maertens Date: Sat, 1 Jan 2022 18:36:55 +0100 Subject: [PATCH 2/2] Allow fetching audio using the play_token --- app/controllers/tracks_controller.rb | 8 +++++++ app/serializers/auth_token_serializer.rb | 2 +- test/controllers/tracks_controller_test.rb | 25 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/controllers/tracks_controller.rb b/app/controllers/tracks_controller.rb index 1fb430fb..f4c205e4 100644 --- a/app/controllers/tracks_controller.rb +++ b/app/controllers/tracks_controller.rb @@ -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' @@ -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) diff --git a/app/serializers/auth_token_serializer.rb b/app/serializers/auth_token_serializer.rb index 909570ca..d8110d02 100644 --- a/app/serializers/auth_token_serializer.rb +++ b/app/serializers/auth_token_serializer.rb @@ -10,5 +10,5 @@ # 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 diff --git a/test/controllers/tracks_controller_test.rb b/test/controllers/tracks_controller_test.rb index e5301686..c7442a46 100644 --- a/test/controllers/tracks_controller_test.rb +++ b/test/controllers/tracks_controller_test.rb @@ -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))