Browse Source

first commit

for_activeadmin
Dmitrii Golub 13 years ago
commit
e4c812ad6d
  1. 20
      .gitignore
  2. 4
      Gemfile
  3. 19
      LICENSE
  4. 48
      README.md
  5. 1
      Rakefile
  6. 43
      app/controllers/devise/two_factor_authentication_controller.rb
  7. 3
      app/views/devise/two_factor_authentication/max_login_attempts_reached.html.erb
  8. 10
      app/views/devise/two_factor_authentication/show.html.erb
  9. 4
      config/locales/en.yml
  10. 27
      lib/two_factor_authentication.rb
  11. 34
      lib/two_factor_authentication/controllers/helpers.rb
  12. 10
      lib/two_factor_authentication/hooks/two_factor_authenticatable.rb
  13. 30
      lib/two_factor_authentication/models/two_factor_authenticatable.rb
  14. 14
      lib/two_factor_authentication/orm/active_record.rb
  15. 7
      lib/two_factor_authentication/rails.rb
  16. 9
      lib/two_factor_authentication/routes.rb
  17. 8
      lib/two_factor_authentication/schema.rb
  18. 3
      lib/two_factor_authentication/version.rb
  19. 26
      two_factor_authentication.gemspec

20
.gitignore vendored

@ -0,0 +1,20 @@
*.gem
.bundle
Gemfile.lock
pkg/*
# Temporary files of every sort
.DS_Store
.idea
.rvmrc
.stgit*
*.swap
*.swo
*.swp
*~
bin/*
nbproject
patches-*
capybara-*.html
dump.rdb
*.ids

4
Gemfile

@ -0,0 +1,4 @@
source "http://rubygems.org"
# Specify your gem's dependencies in devise_ip_filter.gemspec
gemspec

19
LICENSE

@ -0,0 +1,19 @@
Copyright (C) 2012 Dmitrii Golub
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

48
README.md

@ -0,0 +1,48 @@
## Two factor authentication for Devise
## Features
* control sms code pattern
* configure max login attempts
* per user level control if he really need two factor authentication
* your own sms logic
## Configuration
To enable two factor authentication for User model, you should add two_factor_authentication to your devise line, like:
```ruby
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :two_factor_authenticatable
```
Two default parameters
```ruby
config.login_code_random_pattern = /\w+/
config.max_login_attempts = 3
```
Possible random patterns
```ruby
/\d{5}/
/\w{4,8}/
```
see more https://github.com/benburkert/randexp
By default second factor authentication enabled for each user, you can change it with this method in your User mdoel:
```ruby
def need_two_factor_authentication?(request)
request.ip != '127.0.0.1'
end
```
this will disable two factor authentication for local users
Your send sms logic should be in this method in your User model:
```ruby
def send_two_factor_authentication_code(code)
puts code
end
```
This example just puts code in logs

1
Rakefile

@ -0,0 +1 @@
require "bundler/gem_tasks"

43
app/controllers/devise/two_factor_authentication_controller.rb

@ -0,0 +1,43 @@
class Devise::TwoFactorAuthenticationController < DeviseController
prepend_before_filter :authenticate_scope!
before_filter :prepare_and_validate, :handle_two_factor_authentication
def show
end
def update
render :show and return if params[:code].nil?
md5 = Digest::MD5.hexdigest(params[:code])
if md5.eql?(resource.second_factor_pass_code)
warden.session(resource_name)[:need_two_factor_authentication] = false
sign_in resource_name, resource, :bypass => true
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 :notice, :attempt_failed
if resource.max_login_attempts?
sign_out(resource)
render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
else
render :show
end
end
end
private
def authenticate_scope!
self.resource = send("current_#{resource_name}")
end
def prepare_and_validate
redirect_to :root and return if resource.nil?
@limit = resource.class.max_login_attempts
if resource.max_login_attempts?
sign_out(resource)
render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
end
end
end

3
app/views/devise/two_factor_authentication/max_login_attempts_reached.html.erb

@ -0,0 +1,3 @@
<h2>Access completly denied as you have reached your attempts limit = <%= @limit %></h2>
<p>Please contact your system administrator</p>

10
app/views/devise/two_factor_authentication/show.html.erb

@ -0,0 +1,10 @@
<h2>Enter your personal code</h2>
<p><%= flash[:notice] %></p>
<%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
<%= text_field_tag :code %>
<%= submit_tag "Submit" %>
<% end %>
<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>

4
config/locales/en.yml

@ -0,0 +1,4 @@
en:
devise:
two_factor_authentication:
attempt_failed: "Attemp failed"

27
lib/two_factor_authentication.rb

@ -0,0 +1,27 @@
require 'two_factor_authentication/version'
require 'randexp'
require 'devise'
require 'digest'
require 'active_support/concern'
module Devise
mattr_accessor :login_code_random_pattern
@@login_code_random_pattern = /\w+/
mattr_accessor :max_login_attempts
@@max_login_attempts = 3
end
module TwoFactorAuthentication
autoload :Schema, 'two_factor_authentication/schema'
module Controllers
autoload :Helpers, 'two_factor_authentication/controllers/helpers'
end
end
Devise.add_module :two_factor_authenticatable, :model => 'two_factor_authentication/models/two_factor_authenticatable', :controller => :two_factor_authentication, :route => :two_factor_authentication
require 'two_factor_authentication/orm/active_record'
require 'two_factor_authentication/routes'
require 'two_factor_authentication/models/two_factor_authenticatable'
require 'two_factor_authentication/rails'

34
lib/two_factor_authentication/controllers/helpers.rb

@ -0,0 +1,34 @@
module TwoFactorAuthentication
module Controllers
module Helpers
extend ActiveSupport::Concern
included do
before_filter :handle_two_factor_authentication
end
module InstanceMethods
private
def handle_two_factor_authentication
if not request.format.nil? and request.format.html? and not devise_controller?
Devise.mappings.keys.flatten.any? do |scope|
if signed_in?(scope) and warden.session(scope)[:need_two_factor_authentication]
session["#{scope}_return_tor"] = request.path if request.get?
redirect_to two_factor_authentication_path_for(scope)
return
end
end
end
end
def two_factor_authentication_path_for(resource_or_scope = nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
change_path = "#{scope}_two_factor_authentication_path"
send(change_path)
end
end
end
end
end

10
lib/two_factor_authentication/hooks/two_factor_authenticatable.rb

@ -0,0 +1,10 @@
Warden::Manager.after_authentication do |user, auth, options|
if user.respond_to?(:need_two_factor_authentication?)
if auth.session(options[:scope])[:need_two_factor_authentication] = user.need_two_factor_authentication?(auth.request)
code = user.generate_two_factor_code
user.second_factor_pass_code = Digest::MD5.hexdigest(code)
user.save
user.send_two_factor_authentication_code(code)
end
end
end

30
lib/two_factor_authentication/models/two_factor_authenticatable.rb

@ -0,0 +1,30 @@
require 'two_factor_authentication/hooks/two_factor_authenticatable'
module Devise
module Models
module TwoFactorAuthenticatable
extend ActiveSupport::Concern
module ClassMethods
::Devise::Models.config(self, :login_code_random_pattern, :max_login_attempts)
end
module InstanceMethods
def need_two_factor_authentication?
true
end
def generate_two_factor_code
self.class.login_code_random_pattern.gen
end
def send_two_factor_authentication_code(code)
p "Code is #{code}"
end
def max_login_attempts?
second_factor_attempts_count >= self.class.max_login_attempts
end
end
end
end
end

14
lib/two_factor_authentication/orm/active_record.rb

@ -0,0 +1,14 @@
module TwoFactorAuthentication
module Orm
module ActiveRecord
module Schema
include TwoFactorAuthentication::Schema
end
end
end
end
ActiveRecord::ConnectionAdapters::Table.send :include, TwoFactorAuthentication::Orm::ActiveRecord::Schema
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, TwoFactorAuthentication::Orm::ActiveRecord::Schema

7
lib/two_factor_authentication/rails.rb

@ -0,0 +1,7 @@
module TwoFactorAuthentication
class Engine < ::Rails::Engine
ActiveSupport.on_load(:action_controller) do
include TwoFactorAuthentication::Controllers::Helpers
end
end
end

9
lib/two_factor_authentication/routes.rb

@ -0,0 +1,9 @@
module ActionDispatch::Routing
class Mapper
protected
def devise_two_factor_authentication(mapping, controllers)
resource :two_factor_authentication, :only => [:show, :update], :path => mapping.path_names[:two_factor_authentication], :controller => controllers[:two_factor_authentication]
end
end
end

8
lib/two_factor_authentication/schema.rb

@ -0,0 +1,8 @@
module TwoFactorAuthentication
module Schema
def two_factor_authenticatable
apply_devise_schema :second_factor_pass_code, String, :limit => 32
apply_devise_schema :second_factor_attempts_count, Integer, :default => 0
end
end
end

3
lib/two_factor_authentication/version.rb

@ -0,0 +1,3 @@
module TwoFactorAuthentication
VERSION = "0.0.1"
end

26
two_factor_authentication.gemspec

@ -0,0 +1,26 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "two_factor_authentication/version"
Gem::Specification.new do |s|
s.name = "two_factor_authentication"
s.version = TwoFactorAuthentication::VERSION
s.authors = ["Dmitrii Golub"]
s.email = ["dmitrii.golub@gmail.com"]
s.homepage = ""
s.summary = %q{Two factor authentication plugin for devise}
s.description = s.summary
s.rubyforge_project = "two_factor_authentication"
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.add_runtime_dependency 'rails', '>= 3.1.1'
s.add_runtime_dependency 'devise'
s.add_runtime_dependency 'randexp'
s.add_development_dependency 'bundler'
end
Loading…
Cancel
Save