Dmitrii Golub
11 years ago
12 changed files with 223 additions and 55 deletions
@ -1,8 +1,15 @@ |
|||||||
class TwoFactorAuthenticationAddTo<%= table_name.camelize %> < ActiveRecord::Migration |
class TwoFactorAuthenticationAddTo<%= table_name.camelize %> < ActiveRecord::Migration |
||||||
def change |
def up |
||||||
change_table :<%= table_name %> do |t| |
change_table :<%= table_name %> do |t| |
||||||
t.string :second_factor_pass_code , :limit => 32 |
t.string :otp_secret_key |
||||||
t.integer :second_factor_attempts_count, :default => 0 |
t.integer :second_factor_attempts_count, :default => 0 |
||||||
end |
end |
||||||
|
|
||||||
|
add_index :<%= table_name %>, :otp_secret_key, :unique => true |
||||||
|
end |
||||||
|
|
||||||
|
def down |
||||||
|
remove_column :<%= table_name %>, :otp_secret_key |
||||||
|
remove_column :<%= table_name %>, :second_factor_attempts_count |
||||||
end |
end |
||||||
end |
end |
||||||
|
@ -1,10 +1,7 @@ |
|||||||
Warden::Manager.after_authentication do |user, auth, options| |
Warden::Manager.after_authentication do |user, auth, options| |
||||||
if user.respond_to?(:need_two_factor_authentication?) |
if user.respond_to?(:need_two_factor_authentication?) |
||||||
if auth.session(options[:scope])[:need_two_factor_authentication] = user.need_two_factor_authentication?(auth.request) |
if auth.session(options[:scope])[:need_two_factor_authentication] = user.need_two_factor_authentication?(auth.request) |
||||||
code = user.generate_two_factor_code |
user.send_two_factor_authentication_code |
||||||
user.second_factor_pass_code = Digest::MD5.hexdigest(code) |
|
||||||
user.save |
|
||||||
user.send_two_factor_authentication_code(code) |
|
||||||
end |
end |
||||||
end |
end |
||||||
end |
end |
||||||
|
@ -0,0 +1,70 @@ |
|||||||
|
require 'spec_helper' |
||||||
|
include AuthenticatedModelHelper |
||||||
|
|
||||||
|
|
||||||
|
describe Devise::Models::TwoFactorAuthenticatable, '#otp_code' do |
||||||
|
let(:instance) { AuthenticatedModelHelper.create_new_user } |
||||||
|
subject { instance.otp_code(time) } |
||||||
|
let(:time) { 1392852456 } |
||||||
|
|
||||||
|
it "should return an error if no secret is set" do |
||||||
|
expect { |
||||||
|
subject |
||||||
|
}.to raise_error |
||||||
|
end |
||||||
|
|
||||||
|
context "secret is set" do |
||||||
|
before :each do |
||||||
|
instance.otp_secret_key = "2z6hxkdwi3uvrnpn" |
||||||
|
end |
||||||
|
|
||||||
|
it "should not return an error" do |
||||||
|
subject |
||||||
|
end |
||||||
|
|
||||||
|
context "with a known time" do |
||||||
|
let(:time) { 1392852756 } |
||||||
|
|
||||||
|
it "should return a known result" do |
||||||
|
expect(subject).to eq(562202) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe Devise::Models::TwoFactorAuthenticatable, '#authenticate_otp' do |
||||||
|
let(:instance) { AuthenticatedModelHelper.create_new_user } |
||||||
|
|
||||||
|
before :each do |
||||||
|
instance.otp_secret_key = "2z6hxkdwi3uvrnpn" |
||||||
|
end |
||||||
|
|
||||||
|
def do_invoke code, options = {} |
||||||
|
instance.authenticate_otp(code, options) |
||||||
|
end |
||||||
|
|
||||||
|
it "should be able to authenticate a recently created code" do |
||||||
|
code = instance.otp_code |
||||||
|
expect(do_invoke(code)).to eq(true) |
||||||
|
end |
||||||
|
|
||||||
|
it "should not authenticate an old code" do |
||||||
|
code = instance.otp_code(1.minutes.ago.to_i) |
||||||
|
expect(do_invoke(code)).to eq(false) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe Devise::Models::TwoFactorAuthenticatable, '#send_two_factor_authentication_code' do |
||||||
|
|
||||||
|
it "should raise an error by default" do |
||||||
|
instance = AuthenticatedModelHelper.create_new_user |
||||||
|
expect { |
||||||
|
instance.send_two_factor_authentication_code |
||||||
|
}.to raise_error(NotImplementedError) |
||||||
|
end |
||||||
|
|
||||||
|
it "should be overrideable" do |
||||||
|
instance = AuthenticatedModelHelper.create_new_user_with_overrides |
||||||
|
expect(instance.send_two_factor_authentication_code).to eq("Code sent") |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,21 @@ |
|||||||
|
require "rubygems" |
||||||
|
require "bundler/setup" |
||||||
|
|
||||||
|
require 'two_factor_authentication' |
||||||
|
|
||||||
|
|
||||||
|
Dir["#{Dir.pwd}/spec/support/**/*.rb"].each {|f| require f} |
||||||
|
|
||||||
|
|
||||||
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration |
||||||
|
RSpec.configure do |config| |
||||||
|
config.treat_symbols_as_metadata_keys_with_true_values = true |
||||||
|
config.run_all_when_everything_filtered = true |
||||||
|
config.filter_run :focus |
||||||
|
|
||||||
|
# Run specs in random order to surface order dependencies. If you find an |
||||||
|
# order dependency and want to debug it, you can fix the order by providing |
||||||
|
# the seed, which is printed after each run. |
||||||
|
# --seed 1234 |
||||||
|
config.order = 'random' |
||||||
|
end |
@ -0,0 +1,29 @@ |
|||||||
|
module AuthenticatedModelHelper |
||||||
|
|
||||||
|
class User |
||||||
|
extend ActiveModel::Callbacks |
||||||
|
include ActiveModel::Validations |
||||||
|
include Devise::Models::TwoFactorAuthenticatable |
||||||
|
|
||||||
|
define_model_callbacks :create |
||||||
|
attr_accessor :otp_secret_key, :email |
||||||
|
|
||||||
|
has_one_time_password |
||||||
|
end |
||||||
|
|
||||||
|
class UserWithOverrides < User |
||||||
|
|
||||||
|
def send_two_factor_authentication_code |
||||||
|
"Code sent" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def create_new_user |
||||||
|
User.new |
||||||
|
end |
||||||
|
|
||||||
|
def create_new_user_with_overrides |
||||||
|
UserWithOverrides.new |
||||||
|
end |
||||||
|
|
||||||
|
end |
Loading…
Reference in new issue