devise_sms_confirmable 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +57 -62
- data/Rakefile +7 -3
- data/app/controllers/devise/sms_confirmations_controller.rb +1 -0
- data/app/texters/devise/texter.rb +17 -7
- data/app/views/devise/texter/confirmation_instructions.text.erb +1 -2
- data/app/views/devise/texter/{email_changed.text.erb → phone_changed.text.erb} +0 -0
- data/devise_sms_confirmable.gemspec +6 -3
- data/lib/devise_sms_confirmable/models/sms_authenticatable.rb +2 -2
- data/lib/devise_sms_confirmable/models/sms_confirmable.rb +105 -46
- data/lib/devise_sms_confirmable/models/sms_validatable.rb +60 -0
- data/lib/devise_sms_confirmable/version.rb +1 -1
- data/lib/devise_sms_confirmable.rb +17 -1
- metadata +66 -10
- data/.rspec +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7da634ddbe05a536e69cfc4223581ca87f6f22e7aa35e6bbaaa8f8927d16cce
|
4
|
+
data.tar.gz: e779c465edf5eda673f6a90cd50b5273309c903adaebc272e3a500df3526e50d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85925719b646dd4e7482c4d1bc9b8889a7249416504e0884fbead9dc0b4b08499791061552250e5fa326d7789da8a7d108621e521f6c7f27b6a65e071fea60fc
|
7
|
+
data.tar.gz: d87a5c77aef9b90a4e07e29629cc91ce841395538d67e30b80b7ea507ef2686f6f8636c059f688181a2125389c977232d94579b01a59dd614bcb3f5ec5395d29
|
data/Gemfile.lock
CHANGED
@@ -1,56 +1,52 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
devise_sms_confirmable (0.1.
|
4
|
+
devise_sms_confirmable (0.1.2)
|
5
5
|
devise (>= 4.6.2)
|
6
|
-
|
6
|
+
rails (~> 5.1.4)
|
7
7
|
textris (~> 0.7)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
actioncable (5.
|
13
|
-
actionpack (= 5.
|
12
|
+
actioncable (5.1.7)
|
13
|
+
actionpack (= 5.1.7)
|
14
14
|
nio4r (~> 2.0)
|
15
|
-
websocket-driver (
|
16
|
-
actionmailer (5.
|
17
|
-
actionpack (= 5.
|
18
|
-
actionview (= 5.
|
19
|
-
activejob (= 5.
|
15
|
+
websocket-driver (~> 0.6.1)
|
16
|
+
actionmailer (5.1.7)
|
17
|
+
actionpack (= 5.1.7)
|
18
|
+
actionview (= 5.1.7)
|
19
|
+
activejob (= 5.1.7)
|
20
20
|
mail (~> 2.5, >= 2.5.4)
|
21
21
|
rails-dom-testing (~> 2.0)
|
22
|
-
actionpack (5.
|
23
|
-
actionview (= 5.
|
24
|
-
activesupport (= 5.
|
22
|
+
actionpack (5.1.7)
|
23
|
+
actionview (= 5.1.7)
|
24
|
+
activesupport (= 5.1.7)
|
25
25
|
rack (~> 2.0)
|
26
26
|
rack-test (>= 0.6.3)
|
27
27
|
rails-dom-testing (~> 2.0)
|
28
28
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
29
|
-
actionview (5.
|
30
|
-
activesupport (= 5.
|
29
|
+
actionview (5.1.7)
|
30
|
+
activesupport (= 5.1.7)
|
31
31
|
builder (~> 3.1)
|
32
32
|
erubi (~> 1.4)
|
33
33
|
rails-dom-testing (~> 2.0)
|
34
34
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
35
|
-
activejob (5.
|
36
|
-
activesupport (= 5.
|
35
|
+
activejob (5.1.7)
|
36
|
+
activesupport (= 5.1.7)
|
37
37
|
globalid (>= 0.3.6)
|
38
|
-
activemodel (5.
|
39
|
-
activesupport (= 5.
|
40
|
-
activerecord (5.
|
41
|
-
activemodel (= 5.
|
42
|
-
activesupport (= 5.
|
43
|
-
arel (
|
44
|
-
|
45
|
-
actionpack (= 5.2.3)
|
46
|
-
activerecord (= 5.2.3)
|
47
|
-
marcel (~> 0.3.1)
|
48
|
-
activesupport (5.2.3)
|
38
|
+
activemodel (5.1.7)
|
39
|
+
activesupport (= 5.1.7)
|
40
|
+
activerecord (5.1.7)
|
41
|
+
activemodel (= 5.1.7)
|
42
|
+
activesupport (= 5.1.7)
|
43
|
+
arel (~> 8.0)
|
44
|
+
activesupport (5.1.7)
|
49
45
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
50
46
|
i18n (>= 0.7, < 2)
|
51
47
|
minitest (~> 5.1)
|
52
48
|
tzinfo (~> 1.1)
|
53
|
-
arel (
|
49
|
+
arel (8.0.0)
|
54
50
|
bcrypt (3.1.13)
|
55
51
|
builder (3.2.3)
|
56
52
|
concurrent-ruby (1.1.5)
|
@@ -61,24 +57,27 @@ GEM
|
|
61
57
|
railties (>= 4.1.0, < 6.0)
|
62
58
|
responders
|
63
59
|
warden (~> 1.2.3)
|
64
|
-
diff-lcs (1.3)
|
65
60
|
erubi (1.8.0)
|
61
|
+
faraday (0.15.4)
|
62
|
+
multipart-post (>= 1.2, < 3)
|
66
63
|
globalid (0.4.2)
|
67
64
|
activesupport (>= 4.2.0)
|
68
65
|
i18n (1.6.0)
|
69
66
|
concurrent-ruby (~> 1.0)
|
67
|
+
jwt (2.2.1)
|
70
68
|
loofah (2.2.3)
|
71
69
|
crass (~> 1.0.2)
|
72
70
|
nokogiri (>= 1.5.9)
|
73
71
|
mail (2.7.1)
|
74
72
|
mini_mime (>= 0.1.1)
|
75
|
-
|
76
|
-
mimemagic (~> 0.3.2)
|
73
|
+
metaclass (0.0.4)
|
77
74
|
method_source (0.9.2)
|
78
|
-
mimemagic (0.3.3)
|
79
75
|
mini_mime (1.0.2)
|
80
76
|
mini_portile2 (2.4.0)
|
81
77
|
minitest (5.11.3)
|
78
|
+
mocha (1.9.0)
|
79
|
+
metaclass (~> 0.0.1)
|
80
|
+
multipart-post (2.1.1)
|
82
81
|
nio4r (2.4.0)
|
83
82
|
nokogiri (1.10.3)
|
84
83
|
mini_portile2 (~> 2.4.0)
|
@@ -87,49 +86,35 @@ GEM
|
|
87
86
|
rack (2.0.7)
|
88
87
|
rack-test (1.1.0)
|
89
88
|
rack (>= 1.0, < 3)
|
90
|
-
rails (5.
|
91
|
-
actioncable (= 5.
|
92
|
-
actionmailer (= 5.
|
93
|
-
actionpack (= 5.
|
94
|
-
actionview (= 5.
|
95
|
-
activejob (= 5.
|
96
|
-
activemodel (= 5.
|
97
|
-
activerecord (= 5.
|
98
|
-
|
99
|
-
activesupport (= 5.2.3)
|
89
|
+
rails (5.1.7)
|
90
|
+
actioncable (= 5.1.7)
|
91
|
+
actionmailer (= 5.1.7)
|
92
|
+
actionpack (= 5.1.7)
|
93
|
+
actionview (= 5.1.7)
|
94
|
+
activejob (= 5.1.7)
|
95
|
+
activemodel (= 5.1.7)
|
96
|
+
activerecord (= 5.1.7)
|
97
|
+
activesupport (= 5.1.7)
|
100
98
|
bundler (>= 1.3.0)
|
101
|
-
railties (= 5.
|
99
|
+
railties (= 5.1.7)
|
102
100
|
sprockets-rails (>= 2.0.0)
|
103
101
|
rails-dom-testing (2.0.3)
|
104
102
|
activesupport (>= 4.2.0)
|
105
103
|
nokogiri (>= 1.6)
|
106
104
|
rails-html-sanitizer (1.0.4)
|
107
105
|
loofah (~> 2.2, >= 2.2.2)
|
108
|
-
railties (5.
|
109
|
-
actionpack (= 5.
|
110
|
-
activesupport (= 5.
|
106
|
+
railties (5.1.7)
|
107
|
+
actionpack (= 5.1.7)
|
108
|
+
activesupport (= 5.1.7)
|
111
109
|
method_source
|
112
110
|
rake (>= 0.8.7)
|
113
|
-
thor (>= 0.
|
111
|
+
thor (>= 0.18.1, < 2.0)
|
114
112
|
rake (12.3.0)
|
115
113
|
render_anywhere (0.0.12)
|
116
114
|
rails (>= 3.0.7)
|
117
115
|
responders (3.0.0)
|
118
116
|
actionpack (>= 5.0)
|
119
117
|
railties (>= 5.0)
|
120
|
-
rspec (3.7.0)
|
121
|
-
rspec-core (~> 3.7.0)
|
122
|
-
rspec-expectations (~> 3.7.0)
|
123
|
-
rspec-mocks (~> 3.7.0)
|
124
|
-
rspec-core (3.7.1)
|
125
|
-
rspec-support (~> 3.7.0)
|
126
|
-
rspec-expectations (3.7.0)
|
127
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
128
|
-
rspec-support (~> 3.7.0)
|
129
|
-
rspec-mocks (3.7.0)
|
130
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
131
|
-
rspec-support (~> 3.7.0)
|
132
|
-
rspec-support (3.7.1)
|
133
118
|
sprockets (3.7.2)
|
134
119
|
concurrent-ruby (~> 1.0)
|
135
120
|
rack (> 1, < 3)
|
@@ -137,6 +122,7 @@ GEM
|
|
137
122
|
actionpack (>= 4.0)
|
138
123
|
activesupport (>= 4.0)
|
139
124
|
sprockets (>= 3.0.0)
|
125
|
+
sqlite3 (1.4.1)
|
140
126
|
textris (0.7.0)
|
141
127
|
actionmailer (>= 4.0)
|
142
128
|
activejob (>= 4.2)
|
@@ -145,11 +131,16 @@ GEM
|
|
145
131
|
render_anywhere (~> 0.0)
|
146
132
|
thor (0.20.3)
|
147
133
|
thread_safe (0.3.6)
|
134
|
+
timecop (0.9.1)
|
135
|
+
twilio-ruby (5.25.2)
|
136
|
+
faraday (~> 0.9)
|
137
|
+
jwt (>= 1.5, <= 2.5)
|
138
|
+
nokogiri (>= 1.6, < 2.0)
|
148
139
|
tzinfo (1.2.5)
|
149
140
|
thread_safe (~> 0.1)
|
150
141
|
warden (1.2.8)
|
151
142
|
rack (>= 2.0.6)
|
152
|
-
websocket-driver (0.
|
143
|
+
websocket-driver (0.6.5)
|
153
144
|
websocket-extensions (>= 0.1.0)
|
154
145
|
websocket-extensions (0.1.4)
|
155
146
|
|
@@ -158,9 +149,13 @@ PLATFORMS
|
|
158
149
|
|
159
150
|
DEPENDENCIES
|
160
151
|
bundler
|
152
|
+
devise (>= 4.6.2)
|
161
153
|
devise_sms_confirmable!
|
154
|
+
mocha
|
162
155
|
rake
|
163
|
-
|
156
|
+
sqlite3
|
157
|
+
timecop
|
158
|
+
twilio-ruby
|
164
159
|
|
165
160
|
BUNDLED WITH
|
166
161
|
1.17.1
|
data/Rakefile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
-
require "
|
2
|
+
require "rake/testtask"
|
3
3
|
|
4
|
-
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.libs << "lib"
|
7
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
8
|
+
end
|
5
9
|
|
6
|
-
task :default => :
|
10
|
+
task :default => :test
|
@@ -2,41 +2,51 @@
|
|
2
2
|
|
3
3
|
if defined?(Textris)
|
4
4
|
class Devise::Texter < Textris::Base
|
5
|
-
default from:
|
5
|
+
default from: "+48666777888"
|
6
6
|
|
7
7
|
def confirmation_instructions(record, token, opts={})
|
8
8
|
@token = token
|
9
9
|
@resource = record
|
10
10
|
|
11
|
-
|
11
|
+
headers = { to: @resource.phone }.merge(opts)
|
12
|
+
|
13
|
+
text to: headers[:to]
|
12
14
|
end
|
13
15
|
|
14
16
|
def reset_password_instructions(record, token, opts={})
|
15
17
|
@token = token
|
16
18
|
@resource = record
|
17
19
|
|
18
|
-
|
20
|
+
headers = { to: @resource.phone }.merge(opts)
|
21
|
+
|
22
|
+
text to: headers[:to]
|
19
23
|
end
|
20
24
|
|
21
25
|
def unlock_instructions(record, token, opts={})
|
22
26
|
@token = token
|
23
27
|
@resource = record
|
24
28
|
|
25
|
-
|
29
|
+
headers = { to: @resource.phone }.merge(opts)
|
30
|
+
|
31
|
+
text to: headers[:to]
|
26
32
|
end
|
27
33
|
|
28
|
-
def
|
34
|
+
def phone_changed(record, token, opts={})
|
29
35
|
@token = token
|
30
36
|
@resource = record
|
31
37
|
|
32
|
-
|
38
|
+
headers = { to: @resource.phone }.merge(opts)
|
39
|
+
|
40
|
+
text to: headers[:to]
|
33
41
|
end
|
34
42
|
|
35
43
|
def password_change(record, token, opts={})
|
36
44
|
@token = token
|
37
45
|
@resource = record
|
38
46
|
|
39
|
-
|
47
|
+
headers = { to: @resource.phone }.merge(opts)
|
48
|
+
|
49
|
+
text to: headers[:to]
|
40
50
|
end
|
41
51
|
end
|
42
52
|
end
|
File without changes
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
lib = File.expand_path("../lib", __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require "devise_sms_confirmable/version"
|
@@ -24,10 +23,14 @@ Gem::Specification.new do |spec|
|
|
24
23
|
spec.require_paths = ["lib"]
|
25
24
|
|
26
25
|
spec.add_dependency "devise", ">= 4.6.2"
|
27
|
-
spec.add_dependency
|
26
|
+
spec.add_dependency "rails", "~> 5.1.4"
|
28
27
|
spec.add_dependency("textris", "~> 0.7")
|
29
28
|
|
30
29
|
spec.add_development_dependency "bundler"
|
31
30
|
spec.add_development_dependency "rake"
|
32
|
-
spec.add_development_dependency "
|
31
|
+
spec.add_development_dependency "sqlite3"
|
32
|
+
spec.add_development_dependency "devise", ">= 4.6.2"
|
33
|
+
spec.add_development_dependency "twilio-ruby"
|
34
|
+
spec.add_development_dependency "mocha"
|
35
|
+
spec.add_development_dependency "timecop"
|
33
36
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Devise
|
4
4
|
module Models
|
5
|
-
module
|
5
|
+
module SmsAuthenticatable
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
protected
|
@@ -13,7 +13,7 @@ module Devise
|
|
13
13
|
|
14
14
|
def send_sms_devise_notification(notification, *args)
|
15
15
|
message = devise_texter.send(notification, self, *args)
|
16
|
-
message.
|
16
|
+
message.deliver
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -1,19 +1,34 @@
|
|
1
|
+
require_relative 'sms_authenticatable'
|
2
|
+
require_relative 'sms_validatable'
|
3
|
+
|
1
4
|
module Devise
|
2
5
|
module Models
|
3
6
|
module SmsConfirmable
|
4
7
|
extend ActiveSupport::Concern
|
8
|
+
include Devise::Models::SmsAuthenticatable
|
5
9
|
|
6
10
|
included do
|
7
11
|
before_create :generate_sms_confirmation_token, if: :sms_confirmation_required?
|
8
|
-
after_create :
|
12
|
+
after_create :skip_sms_reconfirmation_in_callback!, if: :send_sms_confirmation_notification?
|
13
|
+
before_update :postpone_phone_change_until_confirmation_and_regenerate_sms_confirmation_token, if: :postpone_phone_change?
|
14
|
+
after_update :send_phone_changed_notification, if: :send_phone_changed_notification?
|
15
|
+
|
9
16
|
after_commit :send_on_create_sms_confirmation_instructions, on: :create, if: :send_sms_confirmation_notification?
|
10
17
|
after_commit :send_sms_reconfirmation_instructions, on: :update, if: :sms_reconfirmation_required?
|
11
|
-
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(*args, &block)
|
21
|
+
@bypass_sms_confirmation_postpone = false
|
22
|
+
@skip_sms_reconfirmation_in_callback = false
|
23
|
+
@sms_reconfirmation_required = false
|
24
|
+
@skip_sms_confirmation_notification = false
|
25
|
+
@raw_sms_confirmation_token = nil
|
26
|
+
super
|
12
27
|
end
|
13
28
|
|
14
29
|
def self.required_fields(klass)
|
15
|
-
required_methods = [:sms_confirmed_at, :sms_confirmation_sent_at]
|
16
|
-
required_methods << :unconfirmed_phone if klass.
|
30
|
+
required_methods = [:sms_confirmed_at, :sms_confirmation_sent_at, :sms_confirmation_token]
|
31
|
+
required_methods << :unconfirmed_phone if klass.sms_reconfirmable
|
17
32
|
required_methods
|
18
33
|
end
|
19
34
|
|
@@ -21,18 +36,17 @@ module Devise
|
|
21
36
|
pending_any_sms_confirmation do
|
22
37
|
if sms_confirmation_period_expired?
|
23
38
|
self.errors.add(:phone, :sms_confirmation_period_expired,
|
24
|
-
period: Devise::TimeInflector.time_ago_in_words(self.class.
|
39
|
+
period: Devise::TimeInflector.time_ago_in_words(self.class.sms_confirm_within.ago))
|
25
40
|
return false
|
26
41
|
end
|
27
42
|
|
28
43
|
self.sms_confirmed_at = Time.now.utc
|
29
44
|
|
30
45
|
saved = if pending_sms_reconfirmation?
|
31
|
-
|
46
|
+
skip_sms_reconfirmation!
|
32
47
|
self.phone = unconfirmed_phone
|
33
48
|
self.unconfirmed_phone = nil
|
34
49
|
|
35
|
-
# We need to validate in such cases to enforce e-mail uniqueness
|
36
50
|
save(validate: true)
|
37
51
|
else
|
38
52
|
save(validate: args[:ensure_valid] == true)
|
@@ -49,40 +63,77 @@ module Devise
|
|
49
63
|
end
|
50
64
|
|
51
65
|
def send_sms_reconfirmation_instructions
|
52
|
-
@
|
66
|
+
@sms_reconfirmation_required = false
|
53
67
|
|
54
68
|
unless @skip_confirmation_notification
|
55
69
|
send_sms_confirmation_instructions
|
56
70
|
end
|
57
71
|
end
|
58
72
|
|
73
|
+
def skip_sms_confirmation_notification!
|
74
|
+
@skip_confirmation_notification = true
|
75
|
+
end
|
76
|
+
|
59
77
|
def send_sms_confirmation_instructions
|
60
|
-
unless @
|
61
|
-
|
78
|
+
unless @raw_sms_confirmation_token
|
79
|
+
generate_sms_confirmation_token!
|
62
80
|
end
|
63
81
|
|
64
|
-
opts =
|
65
|
-
send_sms_devise_notification(:confirmation_instructions, @
|
82
|
+
opts = pending_sms_reconfirmation? ? { to: unconfirmed_phone } : { }
|
83
|
+
send_sms_devise_notification(:confirmation_instructions, @raw_sms_confirmation_token, opts)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Resend confirmation token.
|
87
|
+
# Regenerates the token if the period is expired.
|
88
|
+
def resend_sms_confirmation_instructions
|
89
|
+
pending_any_sms_confirmation do
|
90
|
+
send_sms_confirmation_instructions
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def active_for_authentication?
|
95
|
+
|
96
|
+
super && (!sms_confirmation_required? || sms_confirmed? || sms_confirmation_period_valid?)
|
97
|
+
end
|
98
|
+
|
99
|
+
def send_phone_changed_notification
|
100
|
+
send_sms_devise_notification(:phone_changed, to: phone_before_last_save)
|
66
101
|
end
|
67
102
|
|
68
|
-
def
|
103
|
+
def generate_sms_confirmation_token!
|
69
104
|
generate_sms_confirmation_token && save(validate: false)
|
70
105
|
end
|
71
106
|
|
72
107
|
# override Devise::Models::Confirmable#pending_sms_reconfirmation?
|
73
108
|
def pending_sms_reconfirmation?
|
74
|
-
self.class.
|
109
|
+
self.class.sms_reconfirmable && unconfirmed_phone.present?
|
110
|
+
end
|
111
|
+
|
112
|
+
# If you don't want confirmation to be sent on create, neither a code
|
113
|
+
# to be generated, call skip_sms_confirmation!
|
114
|
+
def skip_sms_confirmation!
|
115
|
+
self.sms_confirmed_at = Time.now.utc
|
116
|
+
end
|
117
|
+
|
118
|
+
def skip_sms_reconfirmation!
|
119
|
+
@bypass_sms_confirmation_postpone = true
|
75
120
|
end
|
76
121
|
|
77
|
-
|
122
|
+
protected
|
123
|
+
|
124
|
+
def skip_sms_reconfirmation_in_callback!
|
125
|
+
@skip_sms_reconfirmation_in_callback = true
|
126
|
+
end
|
78
127
|
|
79
128
|
def devise_texter
|
80
129
|
Devise.texter
|
81
130
|
end
|
82
131
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
132
|
+
def sms_confirmation_period_valid?
|
133
|
+
return true if self.class.allow_sms_unconfirmed_access_for.nil?
|
134
|
+
return false if self.class.allow_sms_unconfirmed_access_for == 0.days
|
135
|
+
|
136
|
+
sms_confirmation_sent_at && sms_confirmation_sent_at.utc >= self.class.allow_sms_unconfirmed_access_for.ago
|
86
137
|
end
|
87
138
|
|
88
139
|
# A callback initiated after successfully confirming. This can be
|
@@ -91,7 +142,7 @@ module Devise
|
|
91
142
|
#
|
92
143
|
# Example:
|
93
144
|
#
|
94
|
-
# def
|
145
|
+
# def after_sms_confirmation
|
95
146
|
# self.update_attribute(:invite_code, nil)
|
96
147
|
# end
|
97
148
|
#
|
@@ -107,16 +158,16 @@ module Devise
|
|
107
158
|
end
|
108
159
|
|
109
160
|
def sms_confirmation_period_expired?
|
110
|
-
self.class.
|
161
|
+
self.class.sms_confirm_within && self.sms_confirmation_sent_at && (Time.now.utc > self.sms_confirmation_sent_at.utc + self.class.sms_confirm_within)
|
111
162
|
end
|
112
163
|
|
113
164
|
# Generates a new random token for confirmation, and stores
|
114
165
|
# the time this token is being generated in sms_confirmation_sent_at
|
115
166
|
def generate_sms_confirmation_token
|
116
|
-
if self.
|
117
|
-
@
|
167
|
+
if self.sms_confirmation_token && !sms_confirmation_period_expired?
|
168
|
+
@raw_sms_confirmation_token = self.sms_confirmation_token
|
118
169
|
else
|
119
|
-
self.
|
170
|
+
self.sms_confirmation_token = @raw_sms_confirmation_token = Devise.friendly_token
|
120
171
|
self.sms_confirmation_sent_at = Time.now.utc
|
121
172
|
end
|
122
173
|
end
|
@@ -132,12 +183,13 @@ module Devise
|
|
132
183
|
end
|
133
184
|
|
134
185
|
def postpone_phone_change?
|
135
|
-
postpone = self.class.
|
186
|
+
postpone = self.class.sms_reconfirmable &&
|
136
187
|
will_save_change_to_phone? &&
|
137
|
-
!@
|
188
|
+
!@bypass_sms_confirmation_postpone &&
|
138
189
|
self.phone.present? &&
|
139
|
-
(!@
|
140
|
-
@
|
190
|
+
(!@skip_sms_reconfirmation_in_callback || !self.phone_in_database.nil?)
|
191
|
+
@bypass_sms_confirmation_postpone = false
|
192
|
+
|
141
193
|
postpone
|
142
194
|
end
|
143
195
|
|
@@ -146,51 +198,58 @@ module Devise
|
|
146
198
|
end
|
147
199
|
|
148
200
|
def sms_reconfirmation_required?
|
149
|
-
self.class.
|
201
|
+
self.class.sms_reconfirmable && @sms_reconfirmation_required && (self.phone.present? || self.unconfirmed_phone.present?)
|
150
202
|
end
|
151
203
|
|
152
|
-
def
|
153
|
-
@
|
204
|
+
def postpone_phone_change_until_confirmation_and_regenerate_sms_confirmation_token
|
205
|
+
@sms_reconfirmation_required = true
|
154
206
|
self.unconfirmed_phone = self.phone
|
155
207
|
self.phone = self.phone_in_database
|
156
|
-
self.
|
208
|
+
self.sms_confirmation_token = nil
|
157
209
|
generate_sms_confirmation_token
|
158
210
|
end
|
159
211
|
|
212
|
+
# With reconfirmable, notify the original email when the user first
|
213
|
+
# requests the email change, instead of when the change is confirmed.
|
214
|
+
def send_phone_changed_notification?
|
215
|
+
self.class.sms_reconfirmable && self.class.send_phone_changed_notification && sms_reconfirmation_required?
|
216
|
+
end
|
217
|
+
|
160
218
|
module ClassMethods
|
161
219
|
def send_sms_confirmation_instructions(attributes={})
|
162
|
-
confirmable = find_by_unconfirmed_phone_with_errors(attributes) if
|
220
|
+
confirmable = find_by_unconfirmed_phone_with_errors(attributes) if sms_reconfirmable
|
163
221
|
unless confirmable.try(:persisted?)
|
164
222
|
confirmable = find_or_initialize_with_errors(sms_confirmation_keys, attributes, :not_found)
|
165
223
|
end
|
166
|
-
confirmable.
|
224
|
+
confirmable.resend_sms_confirmation_instructions if confirmable.persisted?
|
167
225
|
confirmable
|
168
226
|
end
|
169
227
|
|
170
|
-
def sms_confirm_by_token(
|
171
|
-
confirmable = find_first_by_auth_conditions(
|
228
|
+
def sms_confirm_by_token(sms_confirmation_token)
|
229
|
+
confirmable = find_first_by_auth_conditions(sms_confirmation_token: sms_confirmation_token)
|
172
230
|
unless confirmable
|
173
|
-
confirmation_digest = Devise.token_generator.digest(self, :
|
174
|
-
confirmable = find_or_initialize_with_error_by(:
|
231
|
+
confirmation_digest = Devise.token_generator.digest(self, :sms_confirmation_token, sms_confirmation_token)
|
232
|
+
confirmable = find_or_initialize_with_error_by(:sms_confirmation_token, confirmation_digest)
|
175
233
|
end
|
176
234
|
|
177
235
|
# TODO: replace above lines with
|
178
|
-
# confirmable = find_or_initialize_with_error_by(:
|
236
|
+
# confirmable = find_or_initialize_with_error_by(:sms_confirmation_token, sms_confirmation_token)
|
179
237
|
# after enough time has passed that Devise clients do not use digested tokens
|
180
238
|
|
181
239
|
confirmable.sms_confirm if confirmable.persisted?
|
182
240
|
confirmable
|
183
241
|
end
|
184
|
-
end
|
185
242
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
243
|
+
def find_by_unconfirmed_phone_with_errors(attributes = {})
|
244
|
+
attributes = attributes.slice(*sms_confirmation_keys).permit!.to_h if attributes.respond_to? :permit
|
245
|
+
unconfirmed_required_attributes = sms_confirmation_keys.map { |k| k == :phone ? :unconfirmed_phone : k }
|
246
|
+
unconfirmed_attributes = attributes.symbolize_keys
|
247
|
+
unconfirmed_attributes[:unconfirmed_phone] = unconfirmed_attributes.delete(:phone)
|
248
|
+
find_or_initialize_with_errors(unconfirmed_required_attributes, unconfirmed_attributes, :not_found)
|
249
|
+
end
|
193
250
|
|
251
|
+
Devise::Models.config(self, :allow_sms_unconfirmed_access_for, :send_phone_changed_notification, :sms_confirmation_keys, :sms_reconfirmable, :sms_confirm_within)
|
252
|
+
end
|
194
253
|
end
|
195
254
|
end
|
196
255
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Models
|
5
|
+
# Validatable creates all needed validations for a user phone and password.
|
6
|
+
# It's optional, given you may want to create the validations by yourself.
|
7
|
+
# Automatically validate if the phone is present, unique and its format is
|
8
|
+
# valid. Also tests presence of password, confirmation and length.
|
9
|
+
#
|
10
|
+
# == Options
|
11
|
+
#
|
12
|
+
# Validatable adds the following options to devise_for:
|
13
|
+
#
|
14
|
+
# * +email_regexp+: the regular expression used to validate e164 format;
|
15
|
+
#
|
16
|
+
#
|
17
|
+
module SmsValidatable
|
18
|
+
# All validations used by this module.
|
19
|
+
VALIDATIONS = [:validates_presence_of, :validates_uniqueness_of, :validates_format_of,
|
20
|
+
:validates_confirmation_of, :validates_length_of].freeze
|
21
|
+
|
22
|
+
def self.required_fields(klass)
|
23
|
+
[]
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.included(base)
|
27
|
+
base.extend ClassMethods
|
28
|
+
assert_validations_api!(base)
|
29
|
+
|
30
|
+
base.class_eval do
|
31
|
+
validates_presence_of :phone, if: :phone_required?
|
32
|
+
validates_uniqueness_of :phone, allow_blank: true, if: :will_save_change_to_phone?
|
33
|
+
validates_format_of :phone, with: e164_phone_regexp, allow_blank: true, if: :will_save_change_to_phone?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.assert_validations_api!(base) #:nodoc:
|
38
|
+
unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
|
39
|
+
|
40
|
+
unless unavailable_validations.empty?
|
41
|
+
raise "Could not use :validatable module since #{base} does not respond " <<
|
42
|
+
"to the following methods: #{unavailable_validations.to_sentence}."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
# Checks whether a password is needed or not. For validations only.
|
49
|
+
# Passwords are always required if it's a new record, or if the password
|
50
|
+
# or confirmation are being set somewhere.
|
51
|
+
def phone_required?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
module ClassMethods
|
56
|
+
Devise::Models.config(self, :e164_phone_regexp)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -7,11 +7,15 @@ require 'devise_sms_confirmable/engine'
|
|
7
7
|
|
8
8
|
module Devise
|
9
9
|
mattr_accessor :sms_confirm_within
|
10
|
-
@@sms_confirm_within =
|
10
|
+
@@sms_confirm_within = nil
|
11
11
|
|
12
12
|
mattr_accessor :sms_confirmation_keys
|
13
13
|
@@sms_confirmation_keys = [:phone]
|
14
14
|
|
15
|
+
# Used to send notification to the original user phone when their phone is changed.
|
16
|
+
mattr_accessor :send_phone_changed_notification
|
17
|
+
@@send_phone_changed_notification = false
|
18
|
+
|
15
19
|
mattr_accessor :parent_texter
|
16
20
|
@@parent_texter = "Textris::Base"
|
17
21
|
|
@@ -19,6 +23,18 @@ module Devise
|
|
19
23
|
mattr_accessor :sms_sender
|
20
24
|
@@sms_sender = nil
|
21
25
|
|
26
|
+
mattr_accessor :e164_phone_regexp
|
27
|
+
@@e164_phone_regexp = /\A\+?[1-9]\d{1,14}\z/
|
28
|
+
|
29
|
+
# Defines if email should be sms_reconfirmable.
|
30
|
+
mattr_accessor :sms_reconfirmable
|
31
|
+
@@sms_reconfirmable = true
|
32
|
+
|
33
|
+
# Time interval you can access your account before confirming your account.
|
34
|
+
# nil - allows unconfirmed access for unlimited time
|
35
|
+
mattr_accessor :allow_sms_unconfirmed_access_for
|
36
|
+
@@allow_sms_unconfirmed_access_for = 0.days
|
37
|
+
|
22
38
|
# Get the sms sender class from the texter reference object.
|
23
39
|
def self.texter
|
24
40
|
@@texter_ref.get
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: devise_sms_confirmable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- matsutani
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: devise
|
@@ -25,19 +25,19 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 4.6.2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rails
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.1.
|
33
|
+
version: 5.1.4
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.1.
|
40
|
+
version: 5.1.4
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: textris
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,7 +81,63 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: sqlite3
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: devise
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 4.6.2
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 4.6.2
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: twilio-ruby
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: mocha
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: timecop
|
85
141
|
requirement: !ruby/object:Gem::Requirement
|
86
142
|
requirements:
|
87
143
|
- - ">="
|
@@ -102,7 +158,6 @@ extensions: []
|
|
102
158
|
extra_rdoc_files: []
|
103
159
|
files:
|
104
160
|
- ".gitignore"
|
105
|
-
- ".rspec"
|
106
161
|
- ".travis.yml"
|
107
162
|
- CODE_OF_CONDUCT.md
|
108
163
|
- Gemfile
|
@@ -113,8 +168,8 @@ files:
|
|
113
168
|
- app/controllers/devise/sms_confirmations_controller.rb
|
114
169
|
- app/texters/devise/texter.rb
|
115
170
|
- app/views/devise/texter/confirmation_instructions.text.erb
|
116
|
-
- app/views/devise/texter/email_changed.text.erb
|
117
171
|
- app/views/devise/texter/password_change.text.erb
|
172
|
+
- app/views/devise/texter/phone_changed.text.erb
|
118
173
|
- app/views/devise/texter/reset_password_instructions.text.erb
|
119
174
|
- app/views/devise/texter/unlock_instructions.text.erb
|
120
175
|
- bin/console
|
@@ -124,6 +179,7 @@ files:
|
|
124
179
|
- lib/devise_sms_confirmable/engine.rb
|
125
180
|
- lib/devise_sms_confirmable/models/sms_authenticatable.rb
|
126
181
|
- lib/devise_sms_confirmable/models/sms_confirmable.rb
|
182
|
+
- lib/devise_sms_confirmable/models/sms_validatable.rb
|
127
183
|
- lib/devise_sms_confirmable/rails/routes.rb
|
128
184
|
- lib/devise_sms_confirmable/version.rb
|
129
185
|
homepage: https://github.com/uuushiro/devise_sms_confirmable
|
data/.rspec
DELETED