Refactor handling of auth token for pre 0.9.9 clients

This commit is contained in:
Marcin Kulik 2015-04-03 16:48:37 +00:00
parent a719251745
commit 47578b63b1
11 changed files with 103 additions and 88 deletions

View File

@ -32,12 +32,7 @@ module Api
private
def asciicast_attributes
username, token = basic_auth_credentials
AsciicastParams.build(params[:asciicast], username, token, request.user_agent)
end
def basic_auth_credentials
authenticate_with_http_basic { |username, password| [username, password] }
AsciicastParams.build(params[:asciicast], current_user, request.user_agent)
end
def asciicast_creator

View File

@ -0,0 +1,25 @@
class ApiTokenRegistrator
def initialize(app)
@app = app
end
def call(env)
auth = Rack::Auth::Basic::Request.new(env)
if auth.provided? && auth.basic? && auth.credentials
ensure_user_with_token(*auth.credentials)
end
@app.call(env)
end
private
def ensure_user_with_token(username, token)
unless ApiToken.where(token: token).exists?
ApiToken.create_with_tmp_user!(token, username)
end
end
end

View File

@ -0,0 +1,26 @@
class MetadataParser
def initialize(app)
@app = app
end
def call(env)
request = Rack::Request.new(env)
auth = Rack::Auth::Basic::Request.new(env)
if request.post? && request.path == '/api/asciicasts'
if request.params['asciicast']['meta'] # pre "format 1" client
meta = JSON.parse(request.params['asciicast']['meta'][:tempfile].read)
request.params['asciicast']['meta'] = meta
username, token = meta.delete('username'), meta.delete('user_token')
if token.present? && !auth.provided? || !auth.basic?
env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(username, token)
end
end
end
@app.call(env)
end
end

View File

@ -8,7 +8,16 @@ class ApiToken < ActiveRecord::Base
validates :token, uniqueness: true
def self.for_token(token)
ApiToken.where(token: token).first
where(token: token).first
end
def self.create_with_tmp_user!(token, username)
transaction do
ApiToken.create!(
token: token,
user: User.create!(temporary_username: username.presence),
)
end
end
def reassign_to(target_user)

View File

@ -1,18 +1,16 @@
class AsciicastParams
FormatError = Class.new(StandardError)
def self.build(asciicast_params, username, token, user_agent)
def self.build(asciicast_params, user, user_agent)
if asciicast_params.try(:respond_to?, :read)
from_format_1_request(asciicast_params, username, token, user_agent)
from_format_1_request(asciicast_params, user, user_agent)
else
from_format_0_request(asciicast_params, username, token, user_agent)
from_format_0_request(asciicast_params, user, user_agent)
end
end
def self.from_format_0_request(params, username, token, user_agent)
meta = JSON.parse(params.delete(:meta).read)
token ||= meta.delete('user_token')
username ||= meta.delete('username')
def self.from_format_0_request(params, user, user_agent)
meta = params.delete(:meta)
attributes = {
command: meta['command'],
@ -26,7 +24,7 @@ class AsciicastParams
terminal_lines: meta['term']['lines'],
terminal_type: meta['term']['type'],
title: meta['title'],
user: User.for_api_token!(token, username),
user: user,
version: 0,
}
@ -39,7 +37,7 @@ class AsciicastParams
attributes
end
def self.from_format_1_request(asciicast_file, username, token, user_agent)
def self.from_format_1_request(asciicast_file, user, user_agent)
begin
asciicast = Oj.sc_parse(AsciicastHandler.new, asciicast_file)
rescue Oj::ParseError
@ -63,7 +61,7 @@ class AsciicastParams
terminal_lines: asciicast['height'],
terminal_type: env && env['TERM'],
title: asciicast['title'],
user: User.for_api_token!(token, username),
user: user,
user_agent: user_agent,
version: version,
}

View File

@ -44,25 +44,10 @@ class User < ActiveRecord::Base
joins(:api_tokens).where('api_tokens.token' => token).first
end
def self.for_api_token!(token, username)
for_api_token(token) || create_with_token(token, username)
end
def self.for_auth_token(auth_token)
where(auth_token: auth_token).first
end
def self.create_with_token(token, username)
return nil if token.blank?
username = nil if username.blank?
transaction do |tx|
user = User.create!(temporary_username: username)
user.api_tokens.create!(token: token)
user
end
end
def self.generate_auth_token
SecureRandom.urlsafe_base64
end

View File

@ -0,0 +1,24 @@
class ApiTokenStrategy < ::Warden::Strategies::Base
def valid?
auth.provided? && auth.basic? && auth.credentials
end
def authenticate!
user = User.for_api_token(auth.credentials.last)
user.nil? ? fail!("Invalid auth token") : success!(user)
end
def store?
false
end
private
def auth
@auth ||= Rack::Auth::Basic::Request.new(env)
end
end
Warden::Strategies.add(:api_token, ApiTokenStrategy)

View File

@ -48,6 +48,10 @@ module Asciinema
rewrite /%7E(.+)/i, '/~$1'
end
config.middleware.use 'MetadataParser'
config.middleware.use 'ApiTokenRegistrator'
config.middleware.use 'Warden::Manager'
config.action_mailer.default_url_options = { protocol: CFG.scheme, host: CFG.host }
if CFG.smtp_settings

View File

@ -7,6 +7,4 @@ Warden::Manager.serialize_from_session do |id|
end
require 'auth_cookie_strategy'
Rails.application.config.middleware.insert_after ActionDispatch::Flash, Warden::Manager do |manager|
end
require 'api_token_strategy'

View File

@ -97,7 +97,7 @@ describe "Asciicast creation" do
end
context 'when a user with given token exists' do
let(:user) { User.create_with_token('f33e6188-f53c-11e2-abf4-84a6c827e88b', 'kill') }
let(:user) { ApiToken.create_with_tmp_user!('f33e6188-f53c-11e2-abf4-84a6c827e88b', 'kill').user }
subject do
user
@ -192,7 +192,7 @@ describe "Asciicast creation" do
end
context 'when a user with given token exists' do
let(:user) { User.create_with_token('f33e6188-f53c-11e2-abf4-84a6c827e88b', 'kill') }
let(:user) { ApiToken.create_with_tmp_user!('f33e6188-f53c-11e2-abf4-84a6c827e88b', 'kill').user }
subject do
user
@ -287,7 +287,7 @@ describe "Asciicast creation" do
end
context 'when a user with given token exists' do
let(:user) { User.create_with_token('f33e6188-f53c-11e2-abf4-84a6c827e88b', 'kill') }
let(:user) { ApiToken.create_with_tmp_user!('f33e6188-f53c-11e2-abf4-84a6c827e88b', 'kill').user }
subject do
user
@ -373,7 +373,7 @@ describe "Asciicast creation" do
end
context 'when a user with given token exists' do
let(:user) { User.create_with_token('f33e6188-f53c-11e2-abf4-84a6c827e88b', 'kill') }
let(:user) { ApiToken.create_with_tmp_user!('f33e6188-f53c-11e2-abf4-84a6c827e88b', 'kill').user }
subject do
user

View File

@ -76,55 +76,6 @@ describe User do
end
end
describe '.create_with_token' do
subject { described_class.create_with_token(token, username) }
let(:token) { 'f33e6188-f53c-11e2-abf4-84a6c827e88b' }
let(:username) { 'somerandomguy' }
it "returns a persisted user record" do
expect(subject.id).not_to be(nil)
end
it "assigns given username to the user as temporary_username" do
expect(subject.temporary_username).to eq(username)
end
it "assigns given api token to the user" do
expect(subject.api_tokens.reload.first.token).to eq(token)
end
context "when token is blank" do
let(:token) { '' }
it { should be(nil) }
end
context "when username is nil" do
let(:username) { nil }
it "returns a persisted user record" do
expect(subject.id).not_to be(nil)
end
it "assigns nil as temporary_username to the user" do
expect(subject.temporary_username).to be(nil)
end
end
context "when username is an empty string" do
let(:username) { nil }
it "returns a persisted user record" do
expect(subject.id).not_to be(nil)
end
it "assigns nil as temporary_username to the user" do
expect(subject.temporary_username).to be(nil)
end
end
end
describe '#username=' do
it 'strips the whitespace' do
user = described_class.new(username: ' sickill ')