devise_google_authenticator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +109 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/app/controllers/devise/checkga_controller.rb +13 -0
- data/app/controllers/devise/displayqr_controller.rb +35 -0
- data/app/views/devise/checkga/show.html.erb +6 -0
- data/app/views/devise/displayqr/show.html.erb +12 -0
- data/devise_google_authenticator.gemspec +82 -0
- data/lib/devise_google_authenticatable/controllers/helpers.rb +17 -0
- data/lib/devise_google_authenticatable/hooks/google_authenticatable.rb +9 -0
- data/lib/devise_google_authenticatable/models/google_authenticatable.rb +44 -0
- data/lib/devise_google_authenticatable/orm/active_record.rb +21 -0
- data/lib/devise_google_authenticatable/patches/check_ga.rb +90 -0
- data/lib/devise_google_authenticatable/patches/display_qr.rb +31 -0
- data/lib/devise_google_authenticatable/patches.rb +13 -0
- data/lib/devise_google_authenticatable/rails.rb +8 -0
- data/lib/devise_google_authenticatable/routes.rb +14 -0
- data/lib/devise_google_authenticatable/schema.rb +30 -0
- data/lib/devise_google_authenticator.rb +26 -0
- data/test/helper.rb +18 -0
- data/test/test_devise_google_authenticator.rb +7 -0
- metadata +153 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
gem "rails"
|
6
|
+
gem "devise"
|
7
|
+
gem "rotp"
|
8
|
+
|
9
|
+
# Add dependencies to develop your gem here.
|
10
|
+
# Include everything needed to run rake, tests, features, etc.
|
11
|
+
group :development do
|
12
|
+
gem "shoulda", ">= 0"
|
13
|
+
gem "bundler", "~> 1.0.0"
|
14
|
+
gem "jeweler", "~> 1.6.4"
|
15
|
+
gem "rcov", ">= 0"
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
actionmailer (3.1.1)
|
5
|
+
actionpack (= 3.1.1)
|
6
|
+
mail (~> 2.3.0)
|
7
|
+
actionpack (3.1.1)
|
8
|
+
activemodel (= 3.1.1)
|
9
|
+
activesupport (= 3.1.1)
|
10
|
+
builder (~> 3.0.0)
|
11
|
+
erubis (~> 2.7.0)
|
12
|
+
i18n (~> 0.6)
|
13
|
+
rack (~> 1.3.2)
|
14
|
+
rack-cache (~> 1.1)
|
15
|
+
rack-mount (~> 0.8.2)
|
16
|
+
rack-test (~> 0.6.1)
|
17
|
+
sprockets (~> 2.0.2)
|
18
|
+
activemodel (3.1.1)
|
19
|
+
activesupport (= 3.1.1)
|
20
|
+
builder (~> 3.0.0)
|
21
|
+
i18n (~> 0.6)
|
22
|
+
activerecord (3.1.1)
|
23
|
+
activemodel (= 3.1.1)
|
24
|
+
activesupport (= 3.1.1)
|
25
|
+
arel (~> 2.2.1)
|
26
|
+
tzinfo (~> 0.3.29)
|
27
|
+
activeresource (3.1.1)
|
28
|
+
activemodel (= 3.1.1)
|
29
|
+
activesupport (= 3.1.1)
|
30
|
+
activesupport (3.1.1)
|
31
|
+
multi_json (~> 1.0)
|
32
|
+
arel (2.2.1)
|
33
|
+
bcrypt-ruby (3.0.1)
|
34
|
+
builder (3.0.0)
|
35
|
+
devise (1.4.9)
|
36
|
+
bcrypt-ruby (~> 3.0)
|
37
|
+
orm_adapter (~> 0.0.3)
|
38
|
+
warden (~> 1.0.3)
|
39
|
+
erubis (2.7.0)
|
40
|
+
git (1.2.5)
|
41
|
+
hike (1.2.1)
|
42
|
+
i18n (0.6.0)
|
43
|
+
jeweler (1.6.4)
|
44
|
+
bundler (~> 1.0)
|
45
|
+
git (>= 1.2.5)
|
46
|
+
rake
|
47
|
+
json (1.6.1)
|
48
|
+
mail (2.3.0)
|
49
|
+
i18n (>= 0.4.0)
|
50
|
+
mime-types (~> 1.16)
|
51
|
+
treetop (~> 1.4.8)
|
52
|
+
mime-types (1.17.2)
|
53
|
+
multi_json (1.0.3)
|
54
|
+
orm_adapter (0.0.5)
|
55
|
+
polyglot (0.3.3)
|
56
|
+
rack (1.3.5)
|
57
|
+
rack-cache (1.1)
|
58
|
+
rack (>= 0.4)
|
59
|
+
rack-mount (0.8.3)
|
60
|
+
rack (>= 1.0.0)
|
61
|
+
rack-ssl (1.3.2)
|
62
|
+
rack
|
63
|
+
rack-test (0.6.1)
|
64
|
+
rack (>= 1.0)
|
65
|
+
rails (3.1.1)
|
66
|
+
actionmailer (= 3.1.1)
|
67
|
+
actionpack (= 3.1.1)
|
68
|
+
activerecord (= 3.1.1)
|
69
|
+
activeresource (= 3.1.1)
|
70
|
+
activesupport (= 3.1.1)
|
71
|
+
bundler (~> 1.0)
|
72
|
+
railties (= 3.1.1)
|
73
|
+
railties (3.1.1)
|
74
|
+
actionpack (= 3.1.1)
|
75
|
+
activesupport (= 3.1.1)
|
76
|
+
rack-ssl (~> 1.3.2)
|
77
|
+
rake (>= 0.8.7)
|
78
|
+
rdoc (~> 3.4)
|
79
|
+
thor (~> 0.14.6)
|
80
|
+
rake (0.9.2.2)
|
81
|
+
rcov (0.9.11)
|
82
|
+
rdoc (3.11)
|
83
|
+
json (~> 1.4)
|
84
|
+
rotp (1.3.2)
|
85
|
+
shoulda (2.11.3)
|
86
|
+
sprockets (2.0.3)
|
87
|
+
hike (~> 1.2)
|
88
|
+
rack (~> 1.0)
|
89
|
+
tilt (~> 1.1, != 1.3.0)
|
90
|
+
thor (0.14.6)
|
91
|
+
tilt (1.3.3)
|
92
|
+
treetop (1.4.10)
|
93
|
+
polyglot
|
94
|
+
polyglot (>= 0.3.1)
|
95
|
+
tzinfo (0.3.31)
|
96
|
+
warden (1.0.6)
|
97
|
+
rack (>= 1.0)
|
98
|
+
|
99
|
+
PLATFORMS
|
100
|
+
ruby
|
101
|
+
|
102
|
+
DEPENDENCIES
|
103
|
+
bundler (~> 1.0.0)
|
104
|
+
devise
|
105
|
+
jeweler (~> 1.6.4)
|
106
|
+
rails
|
107
|
+
rcov
|
108
|
+
rotp
|
109
|
+
shoulda
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Christian Frichot
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= devise_google_authenticator
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to devise_google_authenticator
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
9
|
+
* Fork the project
|
10
|
+
* Start a feature/bugfix branch
|
11
|
+
* Commit and push until you are happy with your contribution
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2011 Christian Frichot. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "devise_google_authenticator"
|
18
|
+
gem.homepage = "http://github.com/xntrik/devise_google_authenticator"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Devise Google Authenticator Extension}
|
21
|
+
gem.description = %Q{Devise Google Authenticator Extension, for adding Google's OTP to your Rails apps!}
|
22
|
+
gem.email = "xntrik@gmail.com"
|
23
|
+
gem.authors = ["Christian Frichot"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
test.rcov_opts << '--exclude "gems/*"'
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "devise_google_authenticator #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Devise::CheckgaController < Devise::SessionsController
|
2
|
+
prepend_before_filter :require_no_authentication, :only => [ :show, :update ]
|
3
|
+
include Devise::Controllers::InternalHelpers
|
4
|
+
|
5
|
+
def show
|
6
|
+
render_with_scope :show
|
7
|
+
end
|
8
|
+
|
9
|
+
def update
|
10
|
+
sign_in(resource_name, resource)
|
11
|
+
respond_with resource, :location => redirect_location(resource_name, resource)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Devise::DisplayqrController < ApplicationController
|
2
|
+
prepend_before_filter :authenticate_scope!, :only => [:show,:update]
|
3
|
+
|
4
|
+
include Devise::Controllers::InternalHelpers
|
5
|
+
|
6
|
+
def show
|
7
|
+
if not resource.nil? and not resource.gauth_secret.nil?
|
8
|
+
render_with_scope :show
|
9
|
+
else
|
10
|
+
sign_in scope, resource, :bypass => true
|
11
|
+
redirect_to stored_location_for(scope) || :root
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def update
|
16
|
+
tmp = params[resource_name]
|
17
|
+
if resource.set_gauth_enabled(params[resource_name])
|
18
|
+
set_flash_message :notice, "Status Updated!"
|
19
|
+
sign_in scope, resource, :bypass => true
|
20
|
+
redirect_to stored_location_for(scope) || :root
|
21
|
+
else
|
22
|
+
render_with_scope :show
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def scope
|
28
|
+
resource_name.to_sym
|
29
|
+
end
|
30
|
+
|
31
|
+
def authenticate_scope!
|
32
|
+
send(:"authenticate_#{resource_name}!")
|
33
|
+
self.resource = send("current_#{resource_name}")
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<h2>Your QR Code</h2>
|
2
|
+
|
3
|
+
<%= google_authenticator_qrcode(resource) %>
|
4
|
+
|
5
|
+
<%= form_for(resource, :as => resource_name, :url => [resource_name, :displayqr], :html => { :method => :put }) do |f| %>
|
6
|
+
<%= devise_error_messages! %>
|
7
|
+
<h3>Would you like to enable Google Authenticator?</h3>
|
8
|
+
<p><%= f.label :gauth_enabled, "Google Authenticator Status" %><br />
|
9
|
+
<%= f.check_box :gauth_enabled %></p>
|
10
|
+
|
11
|
+
<p><%= f.submit "Continue.." %></p>
|
12
|
+
<% end %>
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "devise_google_authenticator"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Christian Frichot"]
|
12
|
+
s.date = "2011-11-21"
|
13
|
+
s.description = "Devise Google Authenticator Extension, for adding Google's OTP to your Rails apps!"
|
14
|
+
s.email = "xntrik@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"app/controllers/devise/checkga_controller.rb",
|
28
|
+
"app/controllers/devise/displayqr_controller.rb",
|
29
|
+
"app/views/devise/checkga/show.html.erb",
|
30
|
+
"app/views/devise/displayqr/show.html.erb",
|
31
|
+
"devise_google_authenticator.gemspec",
|
32
|
+
"lib/devise_google_authenticatable/controllers/helpers.rb",
|
33
|
+
"lib/devise_google_authenticatable/hooks/google_authenticatable.rb",
|
34
|
+
"lib/devise_google_authenticatable/models/google_authenticatable.rb",
|
35
|
+
"lib/devise_google_authenticatable/orm/active_record.rb",
|
36
|
+
"lib/devise_google_authenticatable/patches.rb",
|
37
|
+
"lib/devise_google_authenticatable/patches/check_ga.rb",
|
38
|
+
"lib/devise_google_authenticatable/patches/display_qr.rb",
|
39
|
+
"lib/devise_google_authenticatable/rails.rb",
|
40
|
+
"lib/devise_google_authenticatable/routes.rb",
|
41
|
+
"lib/devise_google_authenticatable/schema.rb",
|
42
|
+
"lib/devise_google_authenticator.rb",
|
43
|
+
"test/helper.rb",
|
44
|
+
"test/test_devise_google_authenticator.rb"
|
45
|
+
]
|
46
|
+
s.homepage = "http://github.com/xntrik/devise_google_authenticator"
|
47
|
+
s.licenses = ["MIT"]
|
48
|
+
s.require_paths = ["lib"]
|
49
|
+
s.rubygems_version = "1.8.10"
|
50
|
+
s.summary = "Devise Google Authenticator Extension"
|
51
|
+
|
52
|
+
if s.respond_to? :specification_version then
|
53
|
+
s.specification_version = 3
|
54
|
+
|
55
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
56
|
+
s.add_runtime_dependency(%q<rails>, [">= 0"])
|
57
|
+
s.add_runtime_dependency(%q<devise>, [">= 0"])
|
58
|
+
s.add_runtime_dependency(%q<rotp>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
61
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
62
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<rails>, [">= 0"])
|
65
|
+
s.add_dependency(%q<devise>, [">= 0"])
|
66
|
+
s.add_dependency(%q<rotp>, [">= 0"])
|
67
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
68
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
69
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
70
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
71
|
+
end
|
72
|
+
else
|
73
|
+
s.add_dependency(%q<rails>, [">= 0"])
|
74
|
+
s.add_dependency(%q<devise>, [">= 0"])
|
75
|
+
s.add_dependency(%q<rotp>, [">= 0"])
|
76
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
77
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
78
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
79
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module DeviseGoogleAuthenticator
|
2
|
+
module Controllers # :nodoc:
|
3
|
+
module Helpers # :nodoc:
|
4
|
+
def google_authenticator_qrcode(user)
|
5
|
+
data = "otpauth://totp/#{username_from_email(user.email)}@#{Rails.application.class.parent_name}?secret=#{user.gauth_secret}"
|
6
|
+
data = Rack::Utils.escape(data)
|
7
|
+
url = "https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=#{data}"
|
8
|
+
return image_tag(url, :alt => 'Google Authenticator QRCode')
|
9
|
+
end
|
10
|
+
|
11
|
+
def username_from_email(email)
|
12
|
+
(/^(.*)@/).match(email)[1]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Warden::Manager.after_set_user do |record, warden, options|
|
2
|
+
if record.respond_to?(:login_phase_one)
|
3
|
+
if warden.session(options[:scope]).fetch(:gauth_phase_one,"nope") == "nope"
|
4
|
+
redirect_to :controller => 'checkga', :action => 'show'
|
5
|
+
end
|
6
|
+
#warden.session(options[:scope])[:gauth_phase_one]
|
7
|
+
#respond_with record, :location => {:controller => 'checkga', :action => 'show'}
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rotp'
|
2
|
+
#require 'devise_google_authenticatable/hooks/google_authenticatable'
|
3
|
+
|
4
|
+
module Devise # :nodoc:
|
5
|
+
module Models # :nodoc:
|
6
|
+
|
7
|
+
module GoogleAuthenticatable
|
8
|
+
|
9
|
+
def self.included(base) # :nodoc:
|
10
|
+
base.extend ClassMethods
|
11
|
+
|
12
|
+
base.class_eval do
|
13
|
+
before_validation :assign_auth_secret, :on => :create
|
14
|
+
include InstanceMethods
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods # :nodoc:
|
19
|
+
def get_qr
|
20
|
+
self.gauth_secret
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_gauth_enabled(param)
|
24
|
+
self.update_without_password(param)
|
25
|
+
end
|
26
|
+
|
27
|
+
def login_phase_one
|
28
|
+
return "yep"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def assign_auth_secret
|
34
|
+
self.gauth_secret = ROTP::Base32.random_base32
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods # :nodoc:
|
40
|
+
#Something here?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DeviseGoogleAuthenticator
|
2
|
+
module Orm
|
3
|
+
# This module contains handle schema (migrations):
|
4
|
+
#
|
5
|
+
# create_table :accounts do |t|
|
6
|
+
# t.gauth_secret
|
7
|
+
# t.gauth_enabled
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
|
11
|
+
module ActiveRecord
|
12
|
+
module Schema
|
13
|
+
include DeviseGoogleAuthenticator::Schema
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ActiveRecord::ConnectionAdapters::Table.send :include, DeviseGoogleAuthenticator::Orm::ActiveRecord::Schema
|
21
|
+
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, DeviseGoogleAuthenticator::Orm::ActiveRecord::Schema
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module DeviseGoogleAuthenticator::Patches
|
2
|
+
# patch Sessions controller to check that the OTP is accurate
|
3
|
+
module CheckGA
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
included do
|
6
|
+
# here the patch
|
7
|
+
|
8
|
+
alias_method :create_original, :create
|
9
|
+
|
10
|
+
#Below is trial 1 .. over-writing most of the create method
|
11
|
+
#Whilst this works, I wish it was about a gazillion times cleaner
|
12
|
+
|
13
|
+
define_method :create do
|
14
|
+
#Okay, firstly we grab the resource, if the user stuffs up anything, this dies immediately.
|
15
|
+
#This actually authenticates their password
|
16
|
+
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
|
17
|
+
|
18
|
+
#Okay, check that the resource model includes the get_qr method
|
19
|
+
if resource.respond_to?(:get_qr) #Therefore we can quiz for a QR
|
20
|
+
|
21
|
+
# Okay, we have the method to get the qr secret, lets check if the user has gauth enabled
|
22
|
+
if resource.gauth_enabled.to_i != 0 #gauth_enabled is not set to zero, therefore it's ON!
|
23
|
+
|
24
|
+
# Orite, At this point the user model includes the extension stuff
|
25
|
+
# PLUS, gauth_enabled is ON, so lets try and authenticate .. but first
|
26
|
+
|
27
|
+
# Lets check if the "POST" includes the gauth_submit parameter
|
28
|
+
if params.fetch(resource_name).include?("gauth_submit") #Yep, the browser submitted the gauth_submit - OTP
|
29
|
+
|
30
|
+
#Okay, lets get what they submitted in the form, crunch it into an int
|
31
|
+
submitted_value = params.fetch(resource_name).fetch("gauth_submit").to_i
|
32
|
+
|
33
|
+
#By default, gauth is not successful
|
34
|
+
gauth_successful = false
|
35
|
+
|
36
|
+
if submitted_value == 0 #We have a field, but they've left it blank..
|
37
|
+
#Nothing, they left the field blank, and therefore will not sign in, gauth_successfull remains false
|
38
|
+
else
|
39
|
+
#Okay, they submitted something into the OTP field
|
40
|
+
|
41
|
+
#Lets account for the fact the timing may not always be accurate, so go backwards, current and forwards
|
42
|
+
#If the submitted OTP matches, then gauth_successful is true - YAY for you! .. Yay for you indeed.
|
43
|
+
|
44
|
+
#CF TODO: Do some checking here, how late can these codes be??
|
45
|
+
if submitted_value == ROTP::TOTP.new(resource.get_qr).at(Time.now.ago(30))
|
46
|
+
gauth_successful = true
|
47
|
+
elsif submitted_value == ROTP::TOTP.new(resource.get_qr).at(Time.now)
|
48
|
+
gauth_successful = true
|
49
|
+
elsif submitted_value == ROTP::TOTP.new(resource.get_qr).at(Time.now.in(30))
|
50
|
+
gauth_successful = true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if gauth_successful == true #That means the OTP actually worked, lets log 'em in
|
55
|
+
set_flash_message(:notice, :signed_in) if is_navigational_format?
|
56
|
+
sign_in(resource_name, resource)
|
57
|
+
respond_with resource, :location => redirect_location(resource_name, resource)
|
58
|
+
else #That means that, the OTP did NOT line up properly, lets kick 'em back to the start
|
59
|
+
signed_in = signed_in?(resource_name)
|
60
|
+
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
|
61
|
+
resource = build_resource
|
62
|
+
clean_up_passwords(resource)
|
63
|
+
respond_with resource, :location => {:controller => 'sessions', :action => 'new'}
|
64
|
+
end
|
65
|
+
|
66
|
+
else #Okay, this is odd, the user is all set to go, but the browser did NOT include the OTP, tampering occurred
|
67
|
+
# OR - the developer did NOT modify the "sessions" view to include
|
68
|
+
# TODO: What should they put in the view again?
|
69
|
+
|
70
|
+
#At this point, we're going to log them back out and send them back to the start
|
71
|
+
signed_in = signed_in?(resource_name)
|
72
|
+
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
|
73
|
+
resource = build_resource
|
74
|
+
clean_up_passwords(resource)
|
75
|
+
respond_with resource, :location => {:controller => 'sessions', :action => 'new'}
|
76
|
+
end
|
77
|
+
else #gauth_enabled must have been set to zero, therefore it's off .. lets just continue with the original sign in process
|
78
|
+
set_flash_message(:notice, :signed_in) if is_navigational_format?
|
79
|
+
sign_in(resource_name, resource)
|
80
|
+
respond_with resource, :location => redirect_location(resource_name, resource)
|
81
|
+
end
|
82
|
+
else #It looks like the model did NOT include the get_qr method .. lets just continue with the original sign in process
|
83
|
+
set_flash_message(:notice, :signed_in) if is_navigational_format?
|
84
|
+
sign_in(resource_name, resource)
|
85
|
+
respond_with resource, :location => redirect_location(resource_name, resource)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module DeviseGoogleAuthenticator::Patches
|
2
|
+
# patch Registrations controller to display the QR code
|
3
|
+
module DisplayQR
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
included do
|
6
|
+
|
7
|
+
#arrr be the patch
|
8
|
+
alias_method :create_original, :create
|
9
|
+
|
10
|
+
define_method :create do
|
11
|
+
build_resource
|
12
|
+
|
13
|
+
if resource.save
|
14
|
+
if resource.active_for_authentication?
|
15
|
+
set_flash_message :notice, :signed_up if is_navigational_format?
|
16
|
+
sign_in(resource_name, resource)
|
17
|
+
|
18
|
+
respond_with resource, :location => {:controller => 'displayqr', :action => 'show'}
|
19
|
+
else
|
20
|
+
set_flash_message :notice, :inactive_signed_up, :reason => inactive_reason(resource) if is_navigational_format?
|
21
|
+
expire_session_data_after_sign_in!
|
22
|
+
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
clean_up_passwords(resource)
|
26
|
+
respond_with_navigational(resource) { render_with_scope :new }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module DeviseGoogleAuthenticator
|
2
|
+
module Patches
|
3
|
+
autoload :DisplayQR, 'devise_google_authenticatable/patches/display_qr'
|
4
|
+
autoload :CheckGA, 'devise_google_authenticatable/patches/check_ga'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def apply
|
8
|
+
Devise::RegistrationsController.send(:include, Patches::DisplayQR)
|
9
|
+
Devise::SessionsController.send(:include, Patches::CheckGA)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActionDispatch::Routing # :nodoc:
|
2
|
+
class Mapper # :nodoc:
|
3
|
+
|
4
|
+
protected
|
5
|
+
|
6
|
+
# route for handle expired passwords
|
7
|
+
def devise_displayqr(mapping, controllers)
|
8
|
+
resource :displayqr, :only => [:show, :update], :path => mapping.path_names[:displayqr], :controller => controllers[:displayqr]
|
9
|
+
resource :checkga, :only => [:show, :update], :path => mapping.path_names[:checkga], :controller => controllers[:checkga]
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module DeviseGoogleAuthenticator
|
2
|
+
# add schema helper for migrations
|
3
|
+
module Schema
|
4
|
+
# Add gauth_secret columns in the resource's database tables
|
5
|
+
#
|
6
|
+
# Examples
|
7
|
+
#
|
8
|
+
# # For a new resource migration:
|
9
|
+
# create_table :the_resources do |t|
|
10
|
+
# t.gauth_secret
|
11
|
+
# t.gauth_enabled
|
12
|
+
# ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# # or if the resource's table already exists, define a migration and put this in:
|
16
|
+
# change_table :the_resources do |t|
|
17
|
+
# t.string :gauth_secret
|
18
|
+
# t.boolean :gauth_enabled
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
def gauth_secret
|
22
|
+
apply_devise_schema :gauth_secret, String
|
23
|
+
end
|
24
|
+
|
25
|
+
def gauth_enabled
|
26
|
+
apply_devise_schema :gauth_enabled, Integer, {:default => 0}
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
2
|
+
require 'active_support/core_ext/integer'
|
3
|
+
require 'active_support/core_ext/string'
|
4
|
+
require 'active_support/ordered_hash'
|
5
|
+
require 'active_support/concern'
|
6
|
+
require 'devise'
|
7
|
+
|
8
|
+
# a security extension for devise
|
9
|
+
module DeviseGoogleAuthenticator
|
10
|
+
autoload :Schema, 'devise_google_authenticatable/schema'
|
11
|
+
autoload :Patches, 'devise_google_authenticatable/patches'
|
12
|
+
|
13
|
+
# module Controllers # :nodoc:
|
14
|
+
# autoload :Helpers, 'devise_google_authenticatable/controllers/helpers'
|
15
|
+
# end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
require 'devise_google_authenticatable/routes'
|
21
|
+
require 'devise_google_authenticatable/rails'
|
22
|
+
require 'devise_google_authenticatable/orm/active_record'
|
23
|
+
require 'devise_google_authenticatable/controllers/helpers'
|
24
|
+
ActionView::Base.send :include, DeviseGoogleAuthenticator::Controllers::Helpers
|
25
|
+
|
26
|
+
Devise.add_module :google_authenticatable, :controller => :google_authenticatable, :model => 'devise_google_authenticatable/models/google_authenticatable', :route => :displayqr
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'devise_google_authenticator'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devise_google_authenticator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Christian Frichot
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-21 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: &70249517217040 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70249517217040
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: devise
|
27
|
+
requirement: &70249517216520 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70249517216520
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rotp
|
38
|
+
requirement: &70249517215940 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70249517215940
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: shoulda
|
49
|
+
requirement: &70249517215320 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70249517215320
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: bundler
|
60
|
+
requirement: &70249517214560 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 1.0.0
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70249517214560
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: jeweler
|
71
|
+
requirement: &70249517213420 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 1.6.4
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70249517213420
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: rcov
|
82
|
+
requirement: &70249517212620 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70249517212620
|
91
|
+
description: Devise Google Authenticator Extension, for adding Google's OTP to your
|
92
|
+
Rails apps!
|
93
|
+
email: xntrik@gmail.com
|
94
|
+
executables: []
|
95
|
+
extensions: []
|
96
|
+
extra_rdoc_files:
|
97
|
+
- LICENSE.txt
|
98
|
+
- README.rdoc
|
99
|
+
files:
|
100
|
+
- .document
|
101
|
+
- Gemfile
|
102
|
+
- Gemfile.lock
|
103
|
+
- LICENSE.txt
|
104
|
+
- README.rdoc
|
105
|
+
- Rakefile
|
106
|
+
- VERSION
|
107
|
+
- app/controllers/devise/checkga_controller.rb
|
108
|
+
- app/controllers/devise/displayqr_controller.rb
|
109
|
+
- app/views/devise/checkga/show.html.erb
|
110
|
+
- app/views/devise/displayqr/show.html.erb
|
111
|
+
- devise_google_authenticator.gemspec
|
112
|
+
- lib/devise_google_authenticatable/controllers/helpers.rb
|
113
|
+
- lib/devise_google_authenticatable/hooks/google_authenticatable.rb
|
114
|
+
- lib/devise_google_authenticatable/models/google_authenticatable.rb
|
115
|
+
- lib/devise_google_authenticatable/orm/active_record.rb
|
116
|
+
- lib/devise_google_authenticatable/patches.rb
|
117
|
+
- lib/devise_google_authenticatable/patches/check_ga.rb
|
118
|
+
- lib/devise_google_authenticatable/patches/display_qr.rb
|
119
|
+
- lib/devise_google_authenticatable/rails.rb
|
120
|
+
- lib/devise_google_authenticatable/routes.rb
|
121
|
+
- lib/devise_google_authenticatable/schema.rb
|
122
|
+
- lib/devise_google_authenticator.rb
|
123
|
+
- test/helper.rb
|
124
|
+
- test/test_devise_google_authenticator.rb
|
125
|
+
homepage: http://github.com/xntrik/devise_google_authenticator
|
126
|
+
licenses:
|
127
|
+
- MIT
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ! '>='
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
segments:
|
139
|
+
- 0
|
140
|
+
hash: -1606279146511615943
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ! '>='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 1.8.10
|
150
|
+
signing_key:
|
151
|
+
specification_version: 3
|
152
|
+
summary: Devise Google Authenticator Extension
|
153
|
+
test_files: []
|