Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration specs #24

Merged
merged 19 commits into from
Apr 8, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
language: ruby

env:
- "RAILS_VERSION=3.2.0"
- "RAILS_VERSION=4.0.0"
- "RAILS_VERSION=master"

rvm:
- 1.9.3
- 2.0
- 2.1

matrix:
allow_failures:
- env: "RAILS_VERSION=master"

before_script:
- bundle exec rake app:db:migrate

script: bundle exec rake spec
17 changes: 17 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,20 @@ source "http://rubygems.org"

# Specify your gem's dependencies in devise_ip_filter.gemspec
gemspec

rails_version = ENV["RAILS_VERSION"] || "default"

rails = case rails_version
when "master"
{github: "rails/rails"}
when "default"
"~> 3.2"
else
"~> #{rails_version}"
end

gem "rails", rails

group :test do
gem "sqlite3"
end
13 changes: 13 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
require "bundler/gem_tasks"

APP_RAKEFILE = File.expand_path("../spec/rails_app/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'

require 'rspec/core/rake_task'

desc "Run all specs in spec directory (excluding plugin specs)"
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')

task :default => :spec

# To test against a specific version of Rails
# export RAILS_VERSION=3.2.0; bundle update; rake
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ def update
if resource.authenticate_otp(params[:code])
warden.session(resource_name)[:need_two_factor_authentication] = false
sign_in resource_name, resource, :bypass => true
set_flash_message :notice, :success
redirect_to stored_location_for(resource_name) || :root
resource.update_attribute(:second_factor_attempts_count, 0)
else
resource.second_factor_attempts_count += 1
resource.save
set_flash_message :error, :attempt_failed
flash.now[:error] = find_message(:attempt_failed)
if resource.max_login_attempts?
sign_out(resource)
render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
render :max_login_attempts_reached
else
render :show
end
Expand All @@ -34,10 +35,10 @@ def authenticate_scope!

def prepare_and_validate
redirect_to :root and return if resource.nil?
@limit = resource.class.max_login_attempts
@limit = resource.max_login_attempts
if resource.max_login_attempts?
sign_out(resource)
render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
render :max_login_attempts_reached and return
end
end
end
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
en:
devise:
two_factor_authentication:
success: "Two factor authentication successful."
attempt_failed: "Attempt failed."
72 changes: 72 additions & 0 deletions spec/features/two_factor_authenticatable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require 'spec_helper'

feature "User of two factor authentication" do

scenario "must be logged in" do
visit user_two_factor_authentication_path

expect(page).to have_content("Welcome Home")
expect(page).to have_content("You are signed out")
end

context "when logged in" do
let(:user) { create_user }

background do
login_as user
end

scenario "can fill in TFA code" do
visit user_two_factor_authentication_path

expect(page).to have_content("You are signed in as Marissa")
expect(page).to have_content("Enter your personal code")

fill_in "code", with: user.otp_code
click_button "Submit"

within(".flash.notice") do
expect(page).to have_content("Two factor authentication successful.")
end
end

scenario "is redirected to TFA when path requires authentication" do
visit dashboard_path

expect(page).to_not have_content("Your Personal Dashboard")

fill_in "code", with: user.otp_code
click_button "Submit"

expect(page).to have_content("Your Personal Dashboard")
expect(page).to have_content("You are signed in as Marissa")
end

scenario "is locked out after max failed attempts" do
visit user_two_factor_authentication_path

max_attempts = User.max_login_attempts

max_attempts.times do
fill_in "code", with: "incorrect#{rand(100)}"
click_button "Submit"

within(".flash.error") do
expect(page).to have_content("Attempt failed")
end
end

expect(page).to have_content("Access completely denied")
expect(page).to have_content("You are signed out")
end

scenario "cannot retry authentication after max attempts" do
user.update_attribute(:second_factor_attempts_count, User.max_login_attempts)

visit user_two_factor_authentication_path

expect(page).to have_content("Access completely denied")
expect(page).to have_content("You are signed out")
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
include AuthenticatedModelHelper

describe Devise::Models::TwoFactorAuthenticatable, '#otp_code' do
let(:instance) { AuthenticatedModelHelper.create_new_user }
let(:instance) { build_guest_user }
subject { instance.otp_code(time) }
let(:time) { 1392852456 }

Expand Down Expand Up @@ -32,7 +32,7 @@
end

describe Devise::Models::TwoFactorAuthenticatable, '#authenticate_otp' do
let(:instance) { AuthenticatedModelHelper.create_new_user }
let(:instance) { build_guest_user }

before :each do
instance.otp_secret_key = "2z6hxkdwi3uvrnpn"
Expand All @@ -54,22 +54,24 @@ def do_invoke code, options = {}
end

describe Devise::Models::TwoFactorAuthenticatable, '#send_two_factor_authentication_code' do
let(:instance) { build_guest_user }

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
def instance.send_two_factor_authentication_code
"Code sent"
end
expect(instance.send_two_factor_authentication_code).to eq("Code sent")
end
end

describe Devise::Models::TwoFactorAuthenticatable, '#provisioning_uri' do
let(:instance) { AuthenticatedModelHelper.create_new_user }
let(:instance) { build_guest_user }

before do
instance.email = "houdini@example.com"
Expand Down Expand Up @@ -99,7 +101,7 @@ def do_invoke code, options = {}
end

describe Devise::Models::TwoFactorAuthenticatable, '#populate_otp_column' do
let(:instance) { AuthenticatedModelHelper.create_new_user }
let(:instance) { build_guest_user }

it "populates otp_column on create" do
expect(instance.otp_secret_key).to be_nil
Expand All @@ -121,14 +123,14 @@ def do_invoke code, options = {}
end

describe Devise::Models::TwoFactorAuthenticatable, '#max_login_attempts' do
let(:instance) { AuthenticatedModelHelper.create_new_user }
let(:instance) { build_guest_user }

before do
@original_max_login_attempts = User.max_login_attempts
User.max_login_attempts = 3
@original_max_login_attempts = GuestUser.max_login_attempts
GuestUser.max_login_attempts = 3
end

after { User.max_login_attempts = @original_max_login_attempts }
after { GuestUser.max_login_attempts = @original_max_login_attempts }

it "returns class setting" do
expect(instance.max_login_attempts).to eq(3)
Expand Down
3 changes: 3 additions & 0 deletions spec/rails_app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
log/
tmp/
*.sqlite3
3 changes: 3 additions & 0 deletions spec/rails_app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Dummy

You have found the dummy rails app used for integration testing of the `two_factor_authentication` gem.
7 changes: 7 additions & 0 deletions spec/rails_app/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require File.expand_path('../config/application', __FILE__)

Dummy::Application.load_tasks
1 change: 1 addition & 0 deletions spec/rails_app/app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//= require_tree .
4 changes: 4 additions & 0 deletions spec/rails_app/app/assets/stylesheets/application.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*
*= require_self
*= require_tree .
*/
3 changes: 3 additions & 0 deletions spec/rails_app/app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ApplicationController < ActionController::Base
protect_from_forgery
end
16 changes: 16 additions & 0 deletions spec/rails_app/app/controllers/home_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class HomeController < ApplicationController
prepend_before_filter :store_location, only: :dashboard
before_filter :authenticate_user!, only: :dashboard

def index
end

def dashboard
end

private

def store_location
store_location_for(:user, dashboard_path)
end
end
8 changes: 8 additions & 0 deletions spec/rails_app/app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module ApplicationHelper

def render_flash
flash.map do |name, message|
content_tag(:p, message, class: "flash #{name}")
end.join.html_safe
end
end
Empty file.
Empty file.
10 changes: 10 additions & 0 deletions spec/rails_app/app/models/guest_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class GuestUser
extend ActiveModel::Callbacks
include ActiveModel::Validations
include Devise::Models::TwoFactorAuthenticatable

define_model_callbacks :create
attr_accessor :otp_secret_key, :email, :second_factor_attempts_count

has_one_time_password
end
11 changes: 11 additions & 0 deletions spec/rails_app/app/models/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class User < ActiveRecord::Base
devise :two_factor_authenticatable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:two_factor_authenticatable

has_one_time_password

def send_two_factor_authentication_code
# No op
end
end
7 changes: 7 additions & 0 deletions spec/rails_app/app/views/home/dashboard.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<h1>Your Personal Dashboard</h1>

<p>Hi <%= current_user.nickname %></p>

<p>Your registered email address is <%= current_user.email %></p>

<p>You can only see this page after successfully completing two factor authentication</p>
3 changes: 3 additions & 0 deletions spec/rails_app/app/views/home/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<h1>Welcome Home</h1>

<p>Find me in app/views/home/index.html.erb</p>
20 changes: 20 additions & 0 deletions spec/rails_app/app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Dummy</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<nav>
<% if user_signed_in? %>
You are signed in as <%= current_user.nickname %>
<% else %>
You are signed out
<% end %>
</nav>
<%= render_flash %>
<%= yield %>
</body>
</html>
4 changes: 4 additions & 0 deletions spec/rails_app/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This file is used by Rack-based servers to start the application.

require ::File.expand_path('../config/environment', __FILE__)
run Dummy::Application
Loading