Skip to content

Instantly share code, notes, and snippets.

@gregnavis gregnavis/routes.rb
Last active Jan 15, 2019

Embed
What would you like to do?
An example how to protect user-uploaded data with a password in a Rails app. This example uses HTTP Basic authentication.
Rails.application.routes.draw do
resources :uploads, only: :show
end
class Upload < ApplicationRecord
has_secure_password validations: false
validates_length_of :password,
maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
def data
"HELLO"
end
end
require 'test_helper'
class UploadTest < ActiveSupport::TestCase
test 'password is optional' do
upload = Upload.create(password: nil)
assert(upload.persisted?)
end
test "password can't be too long" do
upload = Upload.new
upload.password = 'a' * (ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED + 1)
upload.save
assert_not(upload.persisted?)
upload.password = 'a' * ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
upload.save
assert(upload.persisted?)
end
end
class UploadsController < ApplicationController
before_action :authenticate_before_download
def show
send_data upload.data
end
private
def upload
@upload ||= Upload.find(params[:id])
end
def authenticate_before_download
return if upload.password_digest.nil?
realm = "upload-#{upload.id}"
authenticate_or_request_with_http_basic(realm) do |username, password|
upload.authenticate(password)
end
end
end
require 'test_helper'
class UploadsControllerTest < ActionDispatch::IntegrationTest
test 'start download if not password-protected' do
upload = Upload.create!(password: nil)
get upload_path(upload)
assert_response(:success)
assert_equal(upload.data, @response.body)
end
test 'ask for password if password-protected' do
upload = Upload.create!(password: '1234abcd')
get upload_path(upload)
assert_response(:unauthorized)
assert_match(/#{upload.id}/, @response.headers['WWW-Authenticate'])
end
test 'start download if correct password provided' do
upload = Upload.create!(password: '1234abcd')
get upload_path(upload), headers: headers('', upload.password)
assert_response(:success)
assert_equal(upload.data, @response.body)
end
test 'ask for password if incorrect password provided' do
upload = Upload.create!(password: '1234abcd')
get upload_path(upload), headers: headers('', "x#{upload.password}")
assert_response(:unauthorized)
end
test 'ignore user name when authenticating' do
upload = Upload.create!(password: '1234abcd')
get upload_path(upload), headers: headers('greg', upload.password)
assert_response(:success)
end
private
def headers(username, password)
{
'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(username, password)
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.