Introduce ExpiringToken, a base for future authentication

footer-fixes
Marcin Kulik 10 years ago
parent e06e72676e
commit c4a4961553

@ -0,0 +1,28 @@
class ExpiringToken < ActiveRecord::Base
belongs_to :user
validates :user, :token, :expires_at, presence: true
scope :active, -> { where(used_at: nil).where('expires_at > ?', Time.now) }
def self.create_for_user(user)
create!(
user: user,
token: SecureRandom.urlsafe_base64,
expires_at: 15.minutes.from_now
)
end
def self.active_for_token(token)
active.where(token: token).first
end
def use!
raise "token #{token} already used at #{used_at}" if used_at
self.used_at = Time.now
save!
end
end

@ -9,6 +9,7 @@ class User < ActiveRecord::Base
has_many :api_tokens, :dependent => :destroy
has_many :asciicasts, :dependent => :destroy
has_many :comments, :dependent => :destroy
has_many :expiring_tokens, dependent: :destroy
validates :username, uniqueness: { case_sensitive: false },
format: { with: USERNAME_FORMAT },

@ -0,0 +1,13 @@
class CreateAuthCodes < ActiveRecord::Migration
def change
create_table :auth_codes do |t|
t.references :user, index: true, null: false
t.string :code, null: false
t.datetime :expires_at, null: false
t.timestamps
end
add_index :auth_codes, [:code, :expires_at]
end
end

@ -0,0 +1,5 @@
class AddUsedAtToAuthCodes < ActiveRecord::Migration
def change
add_column :auth_codes, :used_at, :datetime
end
end

@ -0,0 +1,5 @@
class RemoveUpdatedAtFromAuthCodes < ActiveRecord::Migration
def change
remove_column :auth_codes, :updated_at
end
end

@ -0,0 +1,6 @@
class UpdateAuthCodesIndex < ActiveRecord::Migration
def change
remove_index :auth_codes, name: "index_auth_codes_on_code_and_expires_at"
add_index :auth_codes, [:used_at, :expires_at, :code]
end
end

@ -0,0 +1,5 @@
class RenameAuthCodesToTemporaryTokens < ActiveRecord::Migration
def change
rename_table :auth_codes, :temporary_tokens
end
end

@ -0,0 +1,5 @@
class RenameTemporaryTokensCodeToToken < ActiveRecord::Migration
def change
rename_column :temporary_tokens, :code, :token
end
end

@ -0,0 +1,5 @@
class RenameTemporaryTokensToExpiringTokens < ActiveRecord::Migration
def change
rename_table :temporary_tokens, :expiring_tokens
end
end

@ -72,6 +72,17 @@ ActiveRecord::Schema.define(version: 20141005152615) do
add_index "comments", ["asciicast_id"], name: "index_comments_on_asciicast_id", using: :btree
add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree
create_table "expiring_tokens", force: true do |t|
t.integer "user_id", null: false
t.string "token", null: false
t.datetime "expires_at", null: false
t.datetime "created_at"
t.datetime "used_at"
end
add_index "expiring_tokens", ["used_at", "expires_at", "token"], name: "index_expiring_tokens_on_used_at_and_expires_at_and_token", using: :btree
add_index "expiring_tokens", ["user_id"], name: "index_expiring_tokens_on_user_id", using: :btree
create_table "likes", force: true do |t|
t.integer "asciicast_id", null: false
t.integer "user_id", null: false

@ -0,0 +1,17 @@
# Read about factories at http://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :expiring_token do
association :user
sequence(:token) { |n| "token-#{n}" }
expires_at { 10.minutes.from_now }
end
factory :used_expiring_token, parent: :expiring_token do
used_at { 1.minute.ago }
end
factory :expired_expiring_token, parent: :expiring_token do
expires_at { 1.minute.ago }
end
end

@ -0,0 +1,47 @@
require 'rails_helper'
RSpec.describe ExpiringToken, :type => :model do
it { should validate_presence_of(:user) }
it { should validate_presence_of(:token) }
it { should validate_presence_of(:expires_at) }
describe '.create_for_user' do
it 'creates expiring token with generated token and expiration time in the future' do
user = create(:user)
expiring_token = ExpiringToken.create_for_user(user)
expect(expiring_token.user).to eq(user)
expect(expiring_token.token.size).to eq(22)
expect(expiring_token.expires_at).to be > Time.now
end
end
describe '.active_for_token' do
it 'returns not used and not expired expiring token matching given token' do
used_expiring_token = create(:used_expiring_token)
expired_expiring_token = create(:expired_expiring_token)
good_expiring_token = create(:expiring_token)
expect(ExpiringToken.active_for_token(used_expiring_token.token)).to be(nil)
expect(ExpiringToken.active_for_token(expired_expiring_token.token)).to be(nil)
expect(ExpiringToken.active_for_token(good_expiring_token.token)).to eq(good_expiring_token)
end
end
describe '#use!' do
it 'sets used_at to the current time and saves the record' do
expiring_token = create(:expiring_token)
now = Time.now
Timecop.freeze(now) do
expiring_token.use!
end
expect(expiring_token.used_at).to eq(now)
expect(expiring_token).to_not be_changed
end
end
end
Loading…
Cancel
Save