active_model_otp 1.0.0 → 1.1.0
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.
- checksums.yaml +5 -13
- data/.travis.yml +1 -2
- data/CHANGELOG.md +1 -0
- data/README.md +9 -1
- data/active_model_otp.gemspec +1 -1
- data/lib/active_model/one_time_password.rb +11 -4
- data/lib/active_model/otp/version.rb +1 -1
- data/test/models/user.rb +0 -1
- data/test/models/visitor.rb +1 -2
- data/test/one_time_password_test.rb +22 -2
- data/test/test_helper.rb +1 -1
- metadata +18 -18
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
ZTQ4OGJlNTQzOTViZWEzNDU1MWU1YmQ3Y2Q5Njc3ZDQ0ZjdmMDVkYQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9d9aedb666900aee840275449fcdb4c2fb912c27
|
4
|
+
data.tar.gz: de9ef98a499709f05a3a836cd50242c74f8b1fdf
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
MmE5YjZlOTlhYmVkMDhkZjNjZjMzOTYzNzkzNTQ0ODZjMGEzOTRjZTc1OGNj
|
11
|
-
NDYwNDRiNmEzNmNjZWJhYTQxNzdhNzE2MzBjYzVhNjFhMzAzMWQ=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
YjdkNDlkYmRjNWM1MTRlZDQzZTMwNzFkZGE4OGMwMzU5NmQ1YWExMDY4YTA2
|
14
|
-
NTMwOTgwYWE5NjdkM2M1MzdiMDc3MGZhMWM0YWUzMDA2NDFlNDFmNWM3ZTlk
|
15
|
-
Y2ViODliODQ4YzQxYmY1Y2UyNWNiYjIzY2QwMDQzZDllNWFhYjQ=
|
6
|
+
metadata.gz: bc0e46abcdae3948c4ea3bf51d92848ecc887d83efb611f7828b830d45f4d2372e0aa45d1c229e627de6e5d404c1ebb626d4c130ed6a3f14980cae6de33da122
|
7
|
+
data.tar.gz: 302bcfde4c0b3d33355cf44ed9b8896a1f83c615c3fafdc9c741ae171955ab78197e5de9b31ac0a4cc7d60fd801c2718c08949f7fc724e8b821e852d10a67179
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#unreleased
|
1
2
|
#v1.0.0
|
2
3
|
- Avoid overriding predefined otp_column value when initializing resource (Ilan Stern) https://github.com/heapsource/active_model_otp/pull/10
|
3
4
|
- Pad OTP codes with less than 6 digits (Johan Brissmyr) https://github.com/heapsource/active_model_otp/pull/7
|
data/README.md
CHANGED
@@ -42,6 +42,14 @@ Note: If you're adding this to an existing user model you'll need to generate *o
|
|
42
42
|
User.all.each { |user| user.update_attribute(:otp_secret_key, ROTP::Base32.random_base32) }
|
43
43
|
```
|
44
44
|
|
45
|
+
For use a custom column for store the secret key field you can us the column_name option
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class User < ActiveRecord::Base
|
49
|
+
has_one_time_password column_name: :my_otp_secret_column
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
45
53
|
|
46
54
|
##Usage
|
47
55
|
|
@@ -133,7 +141,7 @@ puts "Current code #{user.otp_code}"
|
|
133
141
|
- [Generate QR code with rqrcode gem](https://github.com/heapsource/active_model_otp/wiki/Generate-QR-code-with-rqrcode-gem)
|
134
142
|
- Generating QR Code with Google Charts API
|
135
143
|
- [Sendind code via email with Twilio](https://github.com/heapsource/active_model_otp/wiki/Send-code-via-Twilio-SMS)
|
136
|
-
- Using with Mongoid
|
144
|
+
- [Using with Mongoid](https://github.com/heapsource/active_model_otp/wiki/Using-with-Mongoid)
|
137
145
|
|
138
146
|
## Contributing
|
139
147
|
|
data/active_model_otp.gemspec
CHANGED
@@ -3,14 +3,18 @@ module ActiveModel
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
+
|
6
7
|
def has_one_time_password(options = {})
|
7
8
|
|
8
9
|
cattr_accessor :otp_column_name
|
10
|
+
class_attribute :otp_digits
|
11
|
+
|
9
12
|
self.otp_column_name = (options[:column_name] || "otp_secret_key").to_s
|
13
|
+
self.otp_digits = options[:length] || 6
|
10
14
|
|
11
15
|
include InstanceMethodsOnActivation
|
12
16
|
|
13
|
-
before_create { self.
|
17
|
+
before_create { self.otp_regenerate_secret if !self.otp_column}
|
14
18
|
|
15
19
|
if respond_to?(:attributes_protected_by_default)
|
16
20
|
def self.attributes_protected_by_default #:nodoc:
|
@@ -21,8 +25,12 @@ module ActiveModel
|
|
21
25
|
end
|
22
26
|
|
23
27
|
module InstanceMethodsOnActivation
|
28
|
+
def otp_regenerate_secret
|
29
|
+
self.otp_column = ROTP::Base32.random_base32
|
30
|
+
end
|
31
|
+
|
24
32
|
def authenticate_otp(code, options = {})
|
25
|
-
totp = ROTP::TOTP.new(self.otp_column)
|
33
|
+
totp = ROTP::TOTP.new(self.otp_column, {digits: self.otp_digits})
|
26
34
|
if drift = options[:drift]
|
27
35
|
totp.verify_with_drift(code, drift)
|
28
36
|
else
|
@@ -38,7 +46,7 @@ module ActiveModel
|
|
38
46
|
time = options
|
39
47
|
padding = true
|
40
48
|
end
|
41
|
-
ROTP::TOTP.new(self.otp_column).at(time, padding)
|
49
|
+
ROTP::TOTP.new(self.otp_column, {digits: self.otp_digits}).at(time, padding)
|
42
50
|
end
|
43
51
|
|
44
52
|
def provisioning_uri(account = nil)
|
@@ -53,7 +61,6 @@ module ActiveModel
|
|
53
61
|
def otp_column=(attr)
|
54
62
|
self.send("#{self.class.otp_column_name}=", attr)
|
55
63
|
end
|
56
|
-
|
57
64
|
end
|
58
65
|
end
|
59
66
|
end
|
data/test/models/user.rb
CHANGED
data/test/models/visitor.rb
CHANGED
@@ -5,6 +5,7 @@ class OtpTest < MiniTest::Unit::TestCase
|
|
5
5
|
@user = User.new
|
6
6
|
@user.email = 'roberto@heapsource.com'
|
7
7
|
@user.run_callbacks :create
|
8
|
+
|
8
9
|
@visitor = Visitor.new
|
9
10
|
@visitor.email = 'roberto@heapsource.com'
|
10
11
|
@visitor.run_callbacks :create
|
@@ -12,6 +13,7 @@ class OtpTest < MiniTest::Unit::TestCase
|
|
12
13
|
|
13
14
|
def test_authenticate_with_otp
|
14
15
|
code = @user.otp_code
|
16
|
+
|
15
17
|
assert @user.authenticate_otp(code)
|
16
18
|
|
17
19
|
code = @visitor.otp_code
|
@@ -28,13 +30,25 @@ class OtpTest < MiniTest::Unit::TestCase
|
|
28
30
|
|
29
31
|
def test_otp_code
|
30
32
|
assert_match(/^\d{6}$/, @user.otp_code.to_s)
|
31
|
-
assert_match(/^\d{
|
33
|
+
assert_match(/^\d{4}$/, @visitor.otp_code.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_otp_code_with_specific_length
|
37
|
+
assert_match(/^\d{4}$/, @visitor.otp_code(time: 2160, padding: true).to_s)
|
38
|
+
assert_operator(@visitor.otp_code(time: 2160, padding: false).to_s.length, :<= , 4)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_otp_code_without_specific_length
|
42
|
+
assert_match(/^\d{6}$/, @user.otp_code(time: 2160, padding: true).to_s)
|
43
|
+
assert_operator(@user.otp_code(time: 2160, padding: false).to_s.length, :<= , 6)
|
32
44
|
end
|
33
45
|
|
34
46
|
def test_otp_code_padding
|
35
47
|
@user.otp_column = 'kw5jhligwqaiw7jc'
|
36
48
|
assert_match(/^\d{6}$/, @user.otp_code(time: 2160, padding: true).to_s)
|
37
|
-
|
49
|
+
# Modified this spec as it is not guranteed that without padding we will always
|
50
|
+
# get a 3 digit number
|
51
|
+
assert_operator(@user.otp_code(time: 2160, padding: false).to_s.length, :<= , 6)
|
38
52
|
end
|
39
53
|
|
40
54
|
def test_provisioning_uri_with_provided_account
|
@@ -46,4 +60,10 @@ class OtpTest < MiniTest::Unit::TestCase
|
|
46
60
|
assert_match %r{otpauth://totp/roberto@heapsource\.com\?secret=\w{16}}, @user.provisioning_uri
|
47
61
|
assert_match %r{otpauth://totp/roberto@heapsource\.com\?secret=\w{16}}, @visitor.provisioning_uri
|
48
62
|
end
|
63
|
+
|
64
|
+
def test_regenerate_otp
|
65
|
+
secret = @user.otp_column
|
66
|
+
@user.otp_regenerate_secret
|
67
|
+
assert secret != @user.otp_column
|
68
|
+
end
|
49
69
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_model_otp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Guillermo Iguaran
|
@@ -10,78 +10,78 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-10-10 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activemodel
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '0'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: rotp
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - ">="
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: '0'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '0'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: bundler
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - ~>
|
47
|
+
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '1.3'
|
50
50
|
type: :development
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- - ~>
|
54
|
+
- - "~>"
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '1.3'
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: rake
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
|
-
- -
|
61
|
+
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: '0'
|
64
64
|
type: :development
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
67
|
requirements:
|
68
|
-
- -
|
68
|
+
- - ">="
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: minitest
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
|
-
- -
|
75
|
+
- - "~>"
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
77
|
+
version: 5.4.2
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
|
-
- -
|
82
|
+
- - "~>"
|
83
83
|
- !ruby/object:Gem::Version
|
84
|
-
version:
|
84
|
+
version: 5.4.2
|
85
85
|
description: Adds methods to set and authenticate against one time passwords. Inspired
|
86
86
|
in AM::SecurePassword"
|
87
87
|
email:
|
@@ -92,8 +92,8 @@ executables: []
|
|
92
92
|
extensions: []
|
93
93
|
extra_rdoc_files: []
|
94
94
|
files:
|
95
|
-
- .gitignore
|
96
|
-
- .travis.yml
|
95
|
+
- ".gitignore"
|
96
|
+
- ".travis.yml"
|
97
97
|
- CHANGELOG.md
|
98
98
|
- Gemfile
|
99
99
|
- LICENSE.txt
|
@@ -117,12 +117,12 @@ require_paths:
|
|
117
117
|
- lib
|
118
118
|
required_ruby_version: !ruby/object:Gem::Requirement
|
119
119
|
requirements:
|
120
|
-
- -
|
120
|
+
- - ">="
|
121
121
|
- !ruby/object:Gem::Version
|
122
122
|
version: '0'
|
123
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
124
|
requirements:
|
125
|
-
- -
|
125
|
+
- - ">="
|
126
126
|
- !ruby/object:Gem::Version
|
127
127
|
version: '0'
|
128
128
|
requirements: []
|