two_factor_authentication 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+
6
+ # Temporary files of every sort
7
+ .DS_Store
8
+ .idea
9
+ .rvmrc
10
+ .stgit*
11
+ *.swap
12
+ *.swo
13
+ *.swp
14
+ *~
15
+ bin/*
16
+ nbproject
17
+ patches-*
18
+ capybara-*.html
19
+ dump.rdb
20
+ *.ids
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in devise_ip_filter.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Dmitrii Golub
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ ## Two factor authentication for Devise
2
+
3
+ ## Features
4
+
5
+ * control sms code pattern
6
+ * configure max login attempts
7
+ * per user level control if he really need two factor authentication
8
+ * your own sms logic
9
+
10
+ ## Configuration
11
+
12
+ ### Initial Setup
13
+
14
+ In a Rails environment, require the gem in your Gemfile:
15
+
16
+ gem 'two_factor_authentication', git: "http://github.com/Houdini/two_factor_authentication.git"
17
+
18
+ Once that's done, run:
19
+
20
+ bundle install
21
+
22
+
23
+ ### Automatic installation
24
+
25
+ In order to add two factor authorisation to a model, run the command:
26
+
27
+ bundle exec rails g two_factor_authentication MODEL
28
+
29
+ Where MODEL is your model name (e.g. User or Admin). This generator will add `:two_factor_authenticatable` to your model
30
+ and create a migration in `db/migrate/`, which will add `::second_factor_pass_code` and `:second_factor_attempts_count` to your table.
31
+ Finally, run the migration with:
32
+
33
+ bundle exec rake db:migrate
34
+
35
+
36
+ ### Manual installation
37
+
38
+ To manually enable two factor authentication for the User model, you should add two_factor_authentication to your devise line, like:
39
+
40
+ ```ruby
41
+ devise :database_authenticatable, :registerable,
42
+ :recoverable, :rememberable, :trackable, :validatable, :two_factor_authenticatable
43
+ ```
44
+
45
+ Two default parameters
46
+
47
+ ```ruby
48
+ config.login_code_random_pattern = /\w+/
49
+ config.max_login_attempts = 3
50
+ ```
51
+
52
+ Possible random patterns
53
+
54
+ ```ruby
55
+ /\d{5}/
56
+ /\w{4,8}/
57
+ ```
58
+
59
+ see more https://github.com/benburkert/randexp
60
+
61
+ ### Customisation
62
+
63
+ By default second factor authentication enabled for each user, you can change it with this method in your User model:
64
+
65
+ ```ruby
66
+ def need_two_factor_authentication?(request)
67
+ request.ip != '127.0.0.1'
68
+ end
69
+ ```
70
+
71
+ this will disable two factor authentication for local users
72
+
73
+ Your send sms logic should be in this method in your User model:
74
+
75
+ ```ruby
76
+ def send_two_factor_authentication_code(code)
77
+ puts code
78
+ end
79
+ ```
80
+
81
+ This example just puts the code in the logs.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,43 @@
1
+ class Devise::TwoFactorAuthenticationController < DeviseController
2
+ prepend_before_filter :authenticate_scope!
3
+ before_filter :prepare_and_validate, :handle_two_factor_authentication
4
+
5
+ def show
6
+ end
7
+
8
+ def update
9
+ render :show and return if params[:code].nil?
10
+ md5 = Digest::MD5.hexdigest(params[:code])
11
+ if md5.eql?(resource.second_factor_pass_code)
12
+ warden.session(resource_name)[:need_two_factor_authentication] = false
13
+ sign_in resource_name, resource, :bypass => true
14
+ redirect_to stored_location_for(resource_name) || :root
15
+ resource.update_attribute(:second_factor_attempts_count, 0)
16
+ else
17
+ resource.second_factor_attempts_count += 1
18
+ resource.save
19
+ set_flash_message :notice, :attempt_failed
20
+ if resource.max_login_attempts?
21
+ sign_out(resource)
22
+ render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
23
+ else
24
+ render :show
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def authenticate_scope!
32
+ self.resource = send("current_#{resource_name}")
33
+ end
34
+
35
+ def prepare_and_validate
36
+ redirect_to :root and return if resource.nil?
37
+ @limit = resource.class.max_login_attempts
38
+ if resource.max_login_attempts?
39
+ sign_out(resource)
40
+ render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ <h2>Access completly denied as you have reached your attempts limit = <%= @limit %></h2>
2
+ <p>Please contact your system administrator</p>
3
+
@@ -0,0 +1,10 @@
1
+ <h2>Enter your personal code</h2>
2
+
3
+ <p><%= flash[:notice] %></p>
4
+
5
+ <%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
6
+ <%= text_field_tag :code %>
7
+ <%= submit_tag "Submit" %>
8
+ <% end %>
9
+
10
+ <%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
@@ -0,0 +1,4 @@
1
+ en:
2
+ devise:
3
+ two_factor_authentication:
4
+ attempt_failed: "Attemp failed"
@@ -0,0 +1,8 @@
1
+ class TwoFactorAuthenticationAddTo<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def change
3
+ change_table :<%= table_name %> do |t|
4
+ t.string :second_factor_pass_code , :limit => 32
5
+ t.integer :second_factor_attempts_count, :default => 0
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module ActiveRecord
4
+ module Generators
5
+ class TwoFactorAuthenticationGenerator < ActiveRecord::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def copy_two_factor_authentication_migration
9
+ migration_template "migration.rb", "db/migrate/two_factor_authentication_add_to_#{table_name}"
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module TwoFactorAuthenticatable
2
+ module Generators
3
+ class TwoFactorAuthenticationGenerator < Rails::Generators::NamedBase
4
+ namespace "two_factor_authentication"
5
+
6
+ desc "Adds :two_factor_authenticable directive in the given model. It also generates an active record migration."
7
+
8
+ def inject_two_factor_authentication_content
9
+ path = File.join("app", "models", "#{file_path}.rb")
10
+ inject_into_file(path, "two_factor_authenticatable, :", :after => "devise :") if File.exists?(path)
11
+ end
12
+
13
+ hook_for :orm
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ require 'two_factor_authentication/version'
2
+ require 'randexp'
3
+ require 'devise'
4
+ require 'digest'
5
+ require 'active_support/concern'
6
+
7
+ module Devise
8
+ mattr_accessor :login_code_random_pattern
9
+ @@login_code_random_pattern = /\w+/
10
+
11
+ mattr_accessor :max_login_attempts
12
+ @@max_login_attempts = 3
13
+ end
14
+
15
+ module TwoFactorAuthentication
16
+ autoload :Schema, 'two_factor_authentication/schema'
17
+ module Controllers
18
+ autoload :Helpers, 'two_factor_authentication/controllers/helpers'
19
+ end
20
+ end
21
+
22
+ Devise.add_module :two_factor_authenticatable, :model => 'two_factor_authentication/models/two_factor_authenticatable', :controller => :two_factor_authentication, :route => :two_factor_authentication
23
+
24
+ require 'two_factor_authentication/orm/active_record'
25
+ require 'two_factor_authentication/routes'
26
+ require 'two_factor_authentication/models/two_factor_authenticatable'
27
+ require 'two_factor_authentication/rails'
@@ -0,0 +1,32 @@
1
+ module TwoFactorAuthentication
2
+ module Controllers
3
+ module Helpers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_filter :handle_two_factor_authentication
8
+ end
9
+
10
+ private
11
+
12
+ def handle_two_factor_authentication
13
+ if not request.format.nil? and request.format.html? and not devise_controller?
14
+ Devise.mappings.keys.flatten.any? do |scope|
15
+ if signed_in?(scope) and warden.session(scope)[:need_two_factor_authentication]
16
+ session["#{scope}_return_tor"] = request.path if request.get?
17
+ redirect_to two_factor_authentication_path_for(scope)
18
+ return
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def two_factor_authentication_path_for(resource_or_scope = nil)
25
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
26
+ change_path = "#{scope}_two_factor_authentication_path"
27
+ send(change_path)
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,10 @@
1
+ Warden::Manager.after_authentication do |user, auth, options|
2
+ if user.respond_to?(:need_two_factor_authentication?)
3
+ if auth.session(options[:scope])[:need_two_factor_authentication] = user.need_two_factor_authentication?(auth.request)
4
+ code = user.generate_two_factor_code
5
+ user.second_factor_pass_code = Digest::MD5.hexdigest(code)
6
+ user.save
7
+ user.send_two_factor_authentication_code(code)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ require 'two_factor_authentication/hooks/two_factor_authenticatable'
2
+ module Devise
3
+ module Models
4
+ module TwoFactorAuthenticatable
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ ::Devise::Models.config(self, :login_code_random_pattern, :max_login_attempts)
9
+ end
10
+
11
+ def need_two_factor_authentication?
12
+ true
13
+ end
14
+
15
+ def generate_two_factor_code
16
+ self.class.login_code_random_pattern.gen
17
+ end
18
+
19
+ def send_two_factor_authentication_code(code)
20
+ p "Code is #{code}"
21
+ end
22
+
23
+ def max_login_attempts?
24
+ second_factor_attempts_count >= self.class.max_login_attempts
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ module TwoFactorAuthentication
2
+ module Orm
3
+ module ActiveRecord
4
+ module Schema
5
+ include TwoFactorAuthentication::Schema
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ ActiveRecord::ConnectionAdapters::Table.send :include, TwoFactorAuthentication::Orm::ActiveRecord::Schema
12
+ ActiveRecord::ConnectionAdapters::TableDefinition.send :include, TwoFactorAuthentication::Orm::ActiveRecord::Schema
@@ -0,0 +1,7 @@
1
+ module TwoFactorAuthentication
2
+ class Engine < ::Rails::Engine
3
+ ActiveSupport.on_load(:action_controller) do
4
+ include TwoFactorAuthentication::Controllers::Helpers
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ protected
4
+
5
+ def devise_two_factor_authentication(mapping, controllers)
6
+ resource :two_factor_authentication, :only => [:show, :update], :path => mapping.path_names[:two_factor_authentication], :controller => controllers[:two_factor_authentication]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module TwoFactorAuthentication
2
+ module Schema
3
+ def second_factor_pass_code
4
+ apply_devise_schema :second_factor_pass_code, String, :limit => 32
5
+ end
6
+
7
+ def second_factor_attempts_count
8
+ apply_devise_schema :second_factor_attempts_count, Integer, :default => 0
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module TwoFactorAuthentication
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "two_factor_authentication/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "two_factor_authentication"
7
+ s.version = TwoFactorAuthentication::VERSION
8
+ s.authors = ["Dmitrii Golub"]
9
+ s.email = ["dmitrii.golub@gmail.com"]
10
+ s.homepage = "https://github.com/Houdini/two_factor_authentication"
11
+ s.summary = %q{Two factor authentication plugin for devise}
12
+ s.description = <<-EOF
13
+ ### Features ###
14
+ * control sms code pattern
15
+ * configure max login attempts
16
+ * per user level control if he really need two factor authentication
17
+ * your own sms logic
18
+ EOF
19
+
20
+ s.rubyforge_project = "two_factor_authentication"
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+
27
+ s.add_runtime_dependency 'rails', '>= 3.1.1'
28
+ s.add_runtime_dependency 'devise'
29
+ s.add_runtime_dependency 'randexp'
30
+
31
+ s.add_development_dependency 'bundler'
32
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: two_factor_authentication
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dmitrii Golub
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.1.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.1.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: devise
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: randexp
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: ! " ### Features ###\n * control sms code pattern\n * configure
79
+ max login attempts\n * per user level control if he really need two factor authentication\n
80
+ \ * your own sms logic\n"
81
+ email:
82
+ - dmitrii.golub@gmail.com
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - .gitignore
88
+ - Gemfile
89
+ - LICENSE
90
+ - README.md
91
+ - Rakefile
92
+ - app/controllers/devise/two_factor_authentication_controller.rb
93
+ - app/views/devise/two_factor_authentication/max_login_attempts_reached.html.erb
94
+ - app/views/devise/two_factor_authentication/show.html.erb
95
+ - config/locales/en.yml
96
+ - lib/generators/active_record/templates/migration.rb
97
+ - lib/generators/active_record/two_factor_authentication_generator.rb
98
+ - lib/generators/two_factor_authentication/two_factor_authentication_generator.rb
99
+ - lib/two_factor_authentication.rb
100
+ - lib/two_factor_authentication/controllers/helpers.rb
101
+ - lib/two_factor_authentication/hooks/two_factor_authenticatable.rb
102
+ - lib/two_factor_authentication/models/two_factor_authenticatable.rb
103
+ - lib/two_factor_authentication/orm/active_record.rb
104
+ - lib/two_factor_authentication/rails.rb
105
+ - lib/two_factor_authentication/routes.rb
106
+ - lib/two_factor_authentication/schema.rb
107
+ - lib/two_factor_authentication/version.rb
108
+ - two_factor_authentication.gemspec
109
+ homepage: https://github.com/Houdini/two_factor_authentication
110
+ licenses: []
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project: two_factor_authentication
129
+ rubygems_version: 1.8.24
130
+ signing_key:
131
+ specification_version: 3
132
+ summary: Two factor authentication plugin for devise
133
+ test_files: []