active_model_otp 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ matrix:
5
+ include:
6
+ - rvm: jruby
7
+ env: JRUBY_OPTS="--1.9 --server -Xcext.enabled=true"
8
+ - rvm: jruby-head
9
+ env: JRUBY_OPTS="--1.9 --server -Xcext.enabled=true"
10
+ notifications:
11
+ email: false
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.org/heapsource/active_model_otp.png)](https://travis-ci.org/heapsource/active_model_otp)
2
+
1
3
  # ActiveModel::Otp
2
4
 
3
5
  **ActiveModel::Otp** makes adding **Two Factor Authentication**(TFA) to a model simple, let's see what's required to get AMo::Otp working in our Application, using Rails 4.0 (AMo::Otp is also compatible with Rails 3.x versions) we're going to use an User model and some authentication to it. Inspired in AM::SecurePassword
@@ -38,8 +40,9 @@ end
38
40
 
39
41
  ##Usage
40
42
 
41
- The has_one_time_password sentence provides to the model some useful methods in order to implement our TFA system.
42
- The otp_secret_key is saved automatically when a object is created, otp_secret_key is generated according to [RFC 4226](http://tools.ietf.org/html/rfc4226) and the [HOTP RFC](http://tools.ietf.org/html/draft-mraihi-totp-timebased-00). This is compatible with Google Authenticator apps available for Android and iPhone, and now in use on GMail.
43
+ The has_one_time_password sentence provides to the model some useful methods in order to implement our TFA system. AMo:Otp generates one time passwords according to [RFC 4226](http://tools.ietf.org/html/rfc4226) and the [HOTP RFC](http://tools.ietf.org/html/draft-mraihi-totp-timebased-00). This is compatible with Google Authenticator apps available for Android and iPhone, and now in use on GMail.
44
+
45
+ The otp_secret_key is saved automatically when a object is created,
43
46
 
44
47
  ```ruby
45
48
  user = User.create(email: "hello@heapsource.com")
@@ -112,6 +115,8 @@ user.otp_secret_key = "2z6hxkdwi3uvrnpn"
112
115
  puts "Current code #{user.otp_code}"
113
116
  ```
114
117
 
118
+ **Note:** otp_secret_key must be generated using RFC 3548 base32 key strings (for compatilibity with google authenticator)
119
+
115
120
  ### Useful Examples
116
121
 
117
122
  #### Generating QR Code with Google Charts API
@@ -6,7 +6,7 @@ require 'active_model/otp/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "active_model_otp"
8
8
  spec.version = ActiveModel::Otp::VERSION
9
- spec.authors = ["Guillermo Iguaran", "Roberto Miranda", "Firebase.co"]
9
+ spec.authors = ["Guillermo Iguaran", "Roberto Miranda", "Heapsource"]
10
10
  spec.email = ["guilleiguaran@gmail.com", "rjmaltamar@gmail.com", "hello@firebase.co"]
11
11
  spec.description = %q{Adds methods to set and authenticate against one time passwords. Inspired in AM::SecurePassword"}
12
12
  spec.summary = "Adds methods to set and authenticate against one time passwords."
@@ -5,13 +5,16 @@ module ActiveModel
5
5
  module ClassMethods
6
6
  def has_one_time_password(options = {})
7
7
 
8
+ cattr_accessor :otp_column_name
9
+ self.otp_column_name = (options[:column_name] || "otp_secret_key").to_s
10
+
8
11
  include InstanceMethodsOnActivation
9
12
 
10
- before_create { self.otp_secret_key = ROTP::Base32.random_base32 }
13
+ before_create { self.otp_column = ROTP::Base32.random_base32 }
11
14
 
12
15
  if respond_to?(:attributes_protected_by_default)
13
16
  def self.attributes_protected_by_default #:nodoc:
14
- super + ["otp_secret_key"]
17
+ super + [self.otp_column_name]
15
18
  end
16
19
  end
17
20
  end
@@ -19,7 +22,7 @@ module ActiveModel
19
22
 
20
23
  module InstanceMethodsOnActivation
21
24
  def authenticate_otp(code, options = {})
22
- totp = ROTP::TOTP.new(self.otp_secret_key)
25
+ totp = ROTP::TOTP.new(self.otp_column)
23
26
  if drift = options[:drift]
24
27
  totp.verify_with_drift(code, drift)
25
28
  else
@@ -28,12 +31,20 @@ module ActiveModel
28
31
  end
29
32
 
30
33
  def otp_code(time = Time.now)
31
- ROTP::TOTP.new(self.otp_secret_key).at(time)
34
+ ROTP::TOTP.new(self.otp_column).at(time)
32
35
  end
33
36
 
34
37
  def provisioning_uri(account = nil)
35
38
  account ||= self.email if self.respond_to?(:email)
36
- ROTP::TOTP.new(self.otp_secret_key).provisioning_uri(account)
39
+ ROTP::TOTP.new(self.otp_column).provisioning_uri(account)
40
+ end
41
+
42
+ def otp_column
43
+ self.send(self.class.otp_column_name)
44
+ end
45
+
46
+ def otp_column=(attr)
47
+ self.send("#{self.class.otp_column_name}=", attr)
37
48
  end
38
49
 
39
50
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveModel
2
2
  module Otp
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -1,5 +1,6 @@
1
1
  require "active_model"
2
2
  require "active_support/core_ext/class/attribute_accessors"
3
+ require "cgi"
3
4
  require "rotp"
4
5
  require "active_model/one_time_password"
5
6
 
@@ -0,0 +1,12 @@
1
+ class Visitor
2
+ extend ActiveModel::Callbacks
3
+ include ActiveModel::Validations
4
+ include ActiveModel::OneTimePassword
5
+
6
+ define_model_callbacks :create
7
+ attr_accessor :otp_token, :email
8
+
9
+ has_one_time_password :column_name => :otp_token
10
+
11
+ end
12
+
@@ -5,29 +5,39 @@ class OtpTest < MiniTest::Unit::TestCase
5
5
  @user = User.new
6
6
  @user.email = 'roberto@heapsource.com'
7
7
  @user.run_callbacks :create
8
+ @visitor = Visitor.new
9
+ @visitor.email = 'roberto@heapsource.com'
10
+ @visitor.run_callbacks :create
8
11
  end
9
12
 
10
13
  def test_authenticate_with_otp
11
14
  code = @user.otp_code
12
-
13
15
  assert @user.authenticate_otp(code)
16
+
17
+ code = @visitor.otp_code
18
+ assert @visitor.authenticate_otp(code)
14
19
  end
15
20
 
16
21
  def test_authenticate_with_otp_when_drift_is_allowed
17
22
  code = @user.otp_code(Time.now - 30)
18
-
19
23
  assert @user.authenticate_otp(code, drift: 60)
24
+
25
+ code = @visitor.otp_code(Time.now - 30)
26
+ assert @visitor.authenticate_otp(code, drift: 60)
20
27
  end
21
28
 
22
29
  def test_otp_code
23
- assert_match(/\d{6}/, @user.otp_code.to_s)
30
+ assert_match(/\d{5,6}/, @user.otp_code.to_s)
31
+ assert_match(/\d{5,6}/, @visitor.otp_code.to_s)
24
32
  end
25
33
 
26
34
  def test_provisioning_uri_with_provided_account
27
35
  assert_match %r{otpauth://totp/roberto\?secret=\w{16}}, @user.provisioning_uri("roberto")
36
+ assert_match %r{otpauth://totp/roberto\?secret=\w{16}}, @visitor.provisioning_uri("roberto")
28
37
  end
29
38
 
30
39
  def test_provisioning_uri_with_email_field
31
40
  assert_match %r{otpauth://totp/roberto@heapsource\.com\?secret=\w{16}}, @user.provisioning_uri
41
+ assert_match %r{otpauth://totp/roberto@heapsource\.com\?secret=\w{16}}, @visitor.provisioning_uri
32
42
  end
33
43
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_model_otp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Guillermo Iguaran
9
9
  - Roberto Miranda
10
- - Firebase.co
10
+ - Heapsource
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-07-11 00:00:00.000000000 Z
14
+ date: 2013-12-04 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activemodel
@@ -104,6 +104,7 @@ extensions: []
104
104
  extra_rdoc_files: []
105
105
  files:
106
106
  - .gitignore
107
+ - .travis.yml
107
108
  - Gemfile
108
109
  - LICENSE.txt
109
110
  - README.md
@@ -113,6 +114,7 @@ files:
113
114
  - lib/active_model/otp/version.rb
114
115
  - lib/active_model_otp.rb
115
116
  - test/models/user.rb
117
+ - test/models/visitor.rb
116
118
  - test/one_time_password_test.rb
117
119
  - test/test_helper.rb
118
120
  homepage: ''
@@ -142,5 +144,6 @@ specification_version: 3
142
144
  summary: Adds methods to set and authenticate against one time passwords.
143
145
  test_files:
144
146
  - test/models/user.rb
147
+ - test/models/visitor.rb
145
148
  - test/one_time_password_test.rb
146
149
  - test/test_helper.rb