quo_vadis 1.3.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +11 -8
  3. data/CHANGELOG.md +26 -0
  4. data/Gemfile +14 -1
  5. data/Gemfile.lock +178 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +435 -127
  8. data/Rakefile +14 -16
  9. data/app/controllers/quo_vadis/confirmations_controller.rb +56 -0
  10. data/app/controllers/quo_vadis/logs_controller.rb +20 -0
  11. data/app/controllers/quo_vadis/password_resets_controller.rb +65 -0
  12. data/app/controllers/quo_vadis/passwords_controller.rb +26 -0
  13. data/app/controllers/quo_vadis/recovery_codes_controller.rb +54 -0
  14. data/app/controllers/quo_vadis/sessions_controller.rb +50 -132
  15. data/app/controllers/quo_vadis/totps_controller.rb +72 -0
  16. data/app/controllers/quo_vadis/twofas_controller.rb +26 -0
  17. data/app/mailers/quo_vadis/mailer.rb +73 -0
  18. data/app/models/quo_vadis/account.rb +59 -0
  19. data/app/models/quo_vadis/account_confirmation_token.rb +17 -0
  20. data/app/models/quo_vadis/log.rb +57 -0
  21. data/app/models/quo_vadis/password.rb +52 -0
  22. data/app/models/quo_vadis/password_reset_token.rb +17 -0
  23. data/app/models/quo_vadis/recovery_code.rb +26 -0
  24. data/app/models/quo_vadis/session.rb +55 -0
  25. data/app/models/quo_vadis/token.rb +42 -0
  26. data/app/models/quo_vadis/totp.rb +56 -0
  27. data/bin/console +15 -0
  28. data/bin/rails +21 -0
  29. data/bin/setup +8 -0
  30. data/config/locales/quo_vadis.en.yml +51 -18
  31. data/config/routes.rb +40 -12
  32. data/db/migrate/202102150904_setup.rb +48 -0
  33. data/lib/generators/quo_vadis/install_generator.rb +4 -23
  34. data/lib/quo_vadis.rb +100 -106
  35. data/lib/quo_vadis/controller.rb +227 -0
  36. data/lib/quo_vadis/crypt.rb +43 -0
  37. data/lib/quo_vadis/current_request_details.rb +11 -0
  38. data/lib/quo_vadis/defaults.rb +18 -0
  39. data/lib/quo_vadis/encrypted_type.rb +17 -0
  40. data/lib/quo_vadis/engine.rb +9 -11
  41. data/lib/quo_vadis/hmacable.rb +26 -0
  42. data/lib/quo_vadis/ip_masking.rb +31 -0
  43. data/lib/quo_vadis/model.rb +86 -0
  44. data/lib/quo_vadis/version.rb +3 -1
  45. data/quo_vadis.gemspec +18 -24
  46. metadata +49 -229
  47. data/app/controllers/controller_mixin.rb +0 -109
  48. data/app/mailers/quo_vadis/notifier.rb +0 -30
  49. data/app/models/model_mixin.rb +0 -128
  50. data/lib/generators/quo_vadis/templates/migration.rb.erb +0 -18
  51. data/lib/generators/quo_vadis/templates/quo_vadis.rb.erb +0 -96
  52. data/test/dummy/.gitignore +0 -2
  53. data/test/dummy/Rakefile +0 -7
  54. data/test/dummy/app/controllers/application_controller.rb +0 -3
  55. data/test/dummy/app/controllers/articles_controller.rb +0 -20
  56. data/test/dummy/app/controllers/users_controller.rb +0 -17
  57. data/test/dummy/app/helpers/application_helper.rb +0 -2
  58. data/test/dummy/app/helpers/articles_helper.rb +0 -2
  59. data/test/dummy/app/models/article.rb +0 -2
  60. data/test/dummy/app/models/person.rb +0 -3
  61. data/test/dummy/app/models/user.rb +0 -3
  62. data/test/dummy/app/views/articles/index.html.erb +0 -1
  63. data/test/dummy/app/views/articles/new.html.erb +0 -11
  64. data/test/dummy/app/views/layouts/application.html.erb +0 -30
  65. data/test/dummy/app/views/layouts/sessions.html.erb +0 -3
  66. data/test/dummy/app/views/quo_vadis/notifier/change_password.text.erb +0 -9
  67. data/test/dummy/app/views/quo_vadis/notifier/invite.text.erb +0 -8
  68. data/test/dummy/app/views/sessions/edit.html.erb +0 -11
  69. data/test/dummy/app/views/sessions/forgotten.html.erb +0 -13
  70. data/test/dummy/app/views/sessions/invite.html.erb +0 -31
  71. data/test/dummy/app/views/sessions/new.html.erb +0 -15
  72. data/test/dummy/app/views/users/new.html.erb +0 -14
  73. data/test/dummy/config.ru +0 -4
  74. data/test/dummy/config/application.rb +0 -21
  75. data/test/dummy/config/boot.rb +0 -10
  76. data/test/dummy/config/database.yml +0 -22
  77. data/test/dummy/config/environment.rb +0 -5
  78. data/test/dummy/config/environments/development.rb +0 -26
  79. data/test/dummy/config/environments/production.rb +0 -49
  80. data/test/dummy/config/environments/test.rb +0 -37
  81. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  82. data/test/dummy/config/initializers/inflections.rb +0 -10
  83. data/test/dummy/config/initializers/mime_types.rb +0 -5
  84. data/test/dummy/config/initializers/quo_vadis.rb +0 -84
  85. data/test/dummy/config/initializers/rack_patch.rb +0 -16
  86. data/test/dummy/config/initializers/secret_token.rb +0 -7
  87. data/test/dummy/config/initializers/session_store.rb +0 -8
  88. data/test/dummy/config/locales/en.yml +0 -5
  89. data/test/dummy/config/locales/quo_vadis.en.yml +0 -21
  90. data/test/dummy/config/routes.rb +0 -5
  91. data/test/dummy/db/migrate/20110124125037_create_users.rb +0 -13
  92. data/test/dummy/db/migrate/20110124131535_create_articles.rb +0 -14
  93. data/test/dummy/db/migrate/20110127094709_add_authentication_to_users.rb +0 -18
  94. data/test/dummy/db/migrate/20111004112209_create_people.rb +0 -13
  95. data/test/dummy/db/migrate/20111004132342_add_authentication_to_people.rb +0 -18
  96. data/test/dummy/db/schema.rb +0 -33
  97. data/test/dummy/public/404.html +0 -26
  98. data/test/dummy/public/422.html +0 -26
  99. data/test/dummy/public/500.html +0 -26
  100. data/test/dummy/public/favicon.ico +0 -0
  101. data/test/dummy/public/javascripts/application.js +0 -2
  102. data/test/dummy/public/javascripts/controls.js +0 -965
  103. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  104. data/test/dummy/public/javascripts/effects.js +0 -1123
  105. data/test/dummy/public/javascripts/prototype.js +0 -6001
  106. data/test/dummy/public/javascripts/rails.js +0 -175
  107. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  108. data/test/dummy/script/rails +0 -6
  109. data/test/integration/activation_test.rb +0 -108
  110. data/test/integration/authenticate_test.rb +0 -39
  111. data/test/integration/blocked_test.rb +0 -23
  112. data/test/integration/config_test.rb +0 -132
  113. data/test/integration/cookie_test.rb +0 -67
  114. data/test/integration/csrf_test.rb +0 -41
  115. data/test/integration/forgotten_test.rb +0 -93
  116. data/test/integration/helper_test.rb +0 -18
  117. data/test/integration/locale_test.rb +0 -197
  118. data/test/integration/navigation_test.rb +0 -7
  119. data/test/integration/sign_in_person_test.rb +0 -26
  120. data/test/integration/sign_in_test.rb +0 -24
  121. data/test/integration/sign_out_test.rb +0 -20
  122. data/test/integration/sign_up_test.rb +0 -21
  123. data/test/quo_vadis_test.rb +0 -7
  124. data/test/support/integration_case.rb +0 -11
  125. data/test/test_helper.rb +0 -88
  126. data/test/unit/user_test.rb +0 -75
@@ -1,67 +0,0 @@
1
- require 'test_helper'
2
-
3
- class CookieTest < ActiveSupport::IntegrationCase
4
-
5
- test 'authenticated user is remembered between browser sessions' do
6
- user_factory 'Bob', 'bob', 'secret'
7
- sign_in_as 'bob', 'secret'
8
- close_browser
9
- visit root_path
10
- within '#topnav' do
11
- assert page.has_content?('You are signed in as Bob.')
12
- end
13
- assert page.has_no_css?('div.flash')
14
- end
15
-
16
- test "signing in updates the remember-me cookie's expiry time" do
17
- user_factory 'Bob', 'bob', 'secret'
18
- sign_in_as 'bob', 'secret'
19
- cookie_a = get_cookie('remember_me')
20
- assert_equal 2.weeks.from_now.httpdate, cookie_a.expires.httpdate
21
- close_browser
22
- sleep 1 # cookie expiry times are accurate to 1 second.
23
-
24
- sign_in_as 'bob', 'secret'
25
- cookie_b = get_cookie('remember_me')
26
- assert cookie_b.expires > cookie_a.expires
27
- assert_equal cookie_a.value, cookie_b.value
28
- end
29
-
30
- test 'signing out prevents the user being remembered in the next browser session' do
31
- user_factory 'Bob', 'bob', 'secret'
32
- sign_in_as 'bob', 'secret'
33
- visit sign_out_path
34
- close_browser
35
- visit new_article_path
36
- assert_equal sign_in_path, current_path
37
- end
38
-
39
- test "changing user's password prevents user being remembered in the next browser session" do
40
- user_factory 'Bob', 'bob', 'secret'
41
- sign_in_as 'bob', 'secret'
42
- cookie = get_cookie('remember_me')
43
- User.last.update_attributes! :password => 'topsecret'
44
- close_browser
45
- visit new_article_path
46
- assert_equal sign_in_path, current_path
47
- end
48
-
49
- test 'length of time user is remembered can be configured' do
50
- QuoVadis.remember_for = 1.second
51
- user_factory 'Bob', 'bob', 'secret'
52
- sign_in_as 'bob', 'secret'
53
- close_browser
54
- sleep 2
55
- visit new_article_path
56
- assert_equal sign_in_path, current_path
57
- end
58
-
59
- test 'remembering user between sessions can be turned off' do
60
- QuoVadis.remember_for = nil
61
- user_factory 'Bob', 'bob', 'secret'
62
- sign_in_as 'bob', 'secret'
63
- close_browser
64
- visit new_article_path
65
- assert_equal sign_in_path, current_path
66
- end
67
- end
@@ -1,41 +0,0 @@
1
- require 'test_helper'
2
-
3
- class CsrfTest < ActionController::IntegrationTest
4
- setup do
5
- reset_quo_vadis_configuration
6
- end
7
-
8
- test 'cookies are destroyed on unverified requests' do
9
- user_factory 'Bob', 'bob', 'secret'
10
- # sign in
11
- post sign_in_path, :username => 'bob', :password => 'secret'
12
- get new_article_path
13
- assert_equal new_article_path, path
14
-
15
- # mimic closing browser
16
- session.clear
17
-
18
- # assert remember me cookie is still set
19
- assert !cookies['remember_me'].blank?
20
-
21
- # go to new article page, to start new session, and create article
22
- get_via_redirect new_article_path
23
- assert_equal new_article_path, path
24
- assert_difference 'Article.count' do
25
- post articles_path, :article => {:title => 'My article'}, :authenticity_token => session[:_csrf_token]
26
- end
27
-
28
- # assert remember me cookie is still set
29
- assert !cookies['remember_me'].blank?
30
-
31
- # make unverified request
32
- assert_no_difference 'Article.count' do
33
- post articles_path, :article => {:title => 'My article'}, :authenticity_token => 'INVALID'
34
- end
35
-
36
- # assert we are signed out, both at session level and cookie level.
37
- assert cookies['remember_me'].blank?
38
- get_via_redirect new_article_path
39
- assert_equal sign_in_path, path
40
- end
41
- end
@@ -1,93 +0,0 @@
1
- require 'test_helper'
2
-
3
- class ForgottenTest < ActiveSupport::IntegrationCase
4
-
5
- teardown do
6
- Capybara.reset_sessions!
7
- end
8
-
9
- test 'user fills in forgotten-password form with blank username' do
10
- u = user_factory 'Bob', 'bob', 'secret'
11
- u.update_attribute :username, ''
12
- submit_forgotten_details ''
13
- assert_equal forgotten_sign_in_path, current_path
14
- within '.flash.alert' do
15
- assert page.has_content?("Sorry, we did not recognise you.")
16
- end
17
- end
18
-
19
- test 'user fills in forgotten-password form with invalid username' do
20
- submit_forgotten_details 'bob'
21
- assert_equal forgotten_sign_in_path, current_path
22
- within '.flash.alert' do
23
- assert page.has_content?("Sorry, we did not recognise you.")
24
- end
25
- end
26
-
27
- test 'user without email requests password-change email' do
28
- user_factory 'Bob', 'bob', 'secret'
29
- submit_forgotten_details 'bob'
30
- assert_equal forgotten_sign_in_path, current_path
31
- within '.flash.alert' do
32
- assert page.has_content?("Sorry, we don't have an email address for you.")
33
- end
34
- end
35
-
36
- test 'user can request password-change email' do
37
- user_factory 'Bob', 'bob', 'secret', 'bob@example.com'
38
- submit_forgotten_details 'bob'
39
-
40
- assert_equal root_path, current_path
41
- within '.flash.notice' do
42
- assert page.has_content?("We've emailed you a link where you can change your password.")
43
- end
44
- assert !ActionMailer::Base.deliveries.empty?
45
- email = ActionMailer::Base.deliveries.last
46
- assert_equal ['bob@example.com'], email.to
47
- assert_equal ['noreply@example.com'], email.from
48
- assert_equal 'Change your password', email.subject
49
- # Why doesn't this use the default url option set up in test/test_helper.rb#9?
50
- assert_match Regexp.new(Regexp.escape(change_password_url User.last.token, :host => 'www.example.com')), email.encoded
51
- end
52
-
53
- test 'user can follow emailed link while valid to change password' do
54
- user_factory 'Bob', 'bob', 'secret', 'bob@example.com'
55
- submit_forgotten_details 'bob'
56
-
57
- link_in_email = ActionMailer::Base.deliveries.last.encoded[%r{http://.*}].strip
58
- visit link_in_email
59
- fill_in :password, :with => 'topsecret'
60
- click_button 'Change my password'
61
- assert_equal root_path, current_path
62
- within '.flash.notice' do
63
- assert page.has_content?("You have successfully changed your password and you're now signed in.")
64
- end
65
- assert_nil User.last.token
66
- assert_nil User.last.token_created_at
67
- end
68
-
69
- test 'user cannot change password to an invalid one' do
70
- user_factory 'Bob', 'bob', 'secret', 'bob@example.com'
71
- submit_forgotten_details 'bob'
72
-
73
- link_in_email = ActionMailer::Base.deliveries.last.encoded[%r{http://.*}].strip
74
- visit link_in_email
75
- fill_in :password, :with => ''
76
- click_button 'Change my password'
77
- assert_equal change_password_path(User.last.token), current_path
78
- end
79
-
80
- test 'user cannot change password once emailed link is invalid' do
81
- user_factory 'Bob', 'bob', 'secret', 'bob@example.com'
82
- submit_forgotten_details 'bob'
83
- User.last.update_attributes :token_created_at => 1.day.ago
84
-
85
- link_in_email = ActionMailer::Base.deliveries.last.encoded[%r{http://.*}].strip
86
- visit link_in_email
87
- assert_equal forgotten_sign_in_path, current_path
88
- within '.flash.alert' do
89
- assert page.has_content?("Sorry, this link isn't valid anymore.")
90
- end
91
- end
92
-
93
- end
@@ -1,18 +0,0 @@
1
- require 'test_helper'
2
-
3
- class HelperTest < ActiveSupport::IntegrationCase
4
-
5
- teardown do
6
- Capybara.reset_sessions!
7
- end
8
-
9
- test 'Current user helper' do
10
- user_factory 'Bob', 'bob', 'secret'
11
- sign_in_as 'bob', 'secret'
12
-
13
- within '#topnav' do
14
- assert page.has_content?('You are signed in as Bob.')
15
- end
16
- end
17
-
18
- end
@@ -1,197 +0,0 @@
1
- require 'test_helper'
2
-
3
- class LocaleTest < ActiveSupport::IntegrationCase
4
-
5
- teardown do
6
- Capybara.reset_sessions!
7
- end
8
-
9
- test 'sign_in.before flash' do
10
- visit new_article_path
11
- within '.flash' do
12
- assert page.has_content?('Please sign in first.')
13
- end
14
- end
15
-
16
- test 'sign_in.after flash' do
17
- user_factory 'Bob', 'bob', 'secret'
18
- sign_in_as 'bob', 'secret'
19
- within '.flash' do
20
- assert page.has_content?('You have successfully signed in.')
21
- end
22
- end
23
-
24
- test 'sign_in.failed flash' do
25
- sign_in_as 'bob', 'secret'
26
- within '.flash' do
27
- assert page.has_content?('Sorry, we did not recognise you.')
28
- end
29
- end
30
-
31
- test 'sign_out flash' do
32
- visit sign_out_path
33
- within '.flash' do
34
- assert page.has_content?('You have successfully signed out.')
35
- end
36
- end
37
-
38
- test 'sign_in.before flash is optional' do
39
- begin
40
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:sign_in => {:before => ''}}}}
41
- visit new_article_path
42
- assert page.has_no_css?('div.flash')
43
- ensure
44
- I18n.reload!
45
- end
46
- end
47
-
48
- test 'sign_in.after flash is optional' do
49
- user_factory 'Bob', 'bob', 'secret'
50
- begin
51
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:sign_in => {:after => ''}}}}
52
- sign_in_as 'bob', 'secret'
53
- assert page.has_no_css?('div.flash')
54
- ensure
55
- I18n.reload!
56
- end
57
- end
58
-
59
- test 'sign_in.failed flash is optional' do
60
- begin
61
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:sign_in => {:failed => ''}}}}
62
- sign_in_as 'bob', 'secret'
63
- assert page.has_no_css?('div.flash')
64
- ensure
65
- I18n.reload!
66
- end
67
- end
68
-
69
- test 'sign_in.blocked flash' do
70
- QuoVadis.blocked = true
71
- user_factory 'Bob', 'bob', 'secret'
72
- sign_in_as 'bob', 'secret'
73
- within '.flash' do
74
- assert page.has_content?('Sorry, your account is blocked.')
75
- end
76
- end
77
-
78
- test 'sign_in.blocked flash is optional' do
79
- begin
80
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:sign_in => {:blocked => ''}}}}
81
- QuoVadis.blocked = true
82
- user_factory 'Bob', 'bob', 'secret'
83
- sign_in_as 'bob', 'secret'
84
- assert page.has_no_css?('div.flash')
85
- ensure
86
- I18n.reload!
87
- end
88
- end
89
-
90
- test 'sign_out flash is optional' do
91
- begin
92
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:sign_out => ''}}}
93
- visit sign_out_path
94
- assert page.has_no_css?('div.flash')
95
- ensure
96
- I18n.reload!
97
- end
98
- end
99
-
100
- test 'forgotten.unknown flash' do
101
- submit_forgotten_details 'bob'
102
- within '.flash.alert' do
103
- assert page.has_content?('Sorry, we did not recognise you.')
104
- end
105
- end
106
-
107
- test 'forgotten.unknown flash is optional' do
108
- begin
109
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:forgotten => {:unknown => ''}}}}
110
- submit_forgotten_details 'bob'
111
- assert page.has_no_css?('div.flash')
112
- ensure
113
- I18n.reload!
114
- end
115
- end
116
-
117
- test 'forgotten.no_email flash' do
118
- user_factory 'Bob', 'bob', 'secret'
119
- submit_forgotten_details 'bob'
120
- within '.flash.alert' do
121
- assert page.has_content?("Sorry, we don't have an email address for you.")
122
- end
123
- end
124
-
125
- test 'forgotten.no_email flash is optional' do
126
- begin
127
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:forgotten => {:no_email => ''}}}}
128
- user_factory 'Bob', 'bob', 'secret'
129
- submit_forgotten_details 'bob'
130
- assert page.has_no_css?('div.flash')
131
- ensure
132
- I18n.reload!
133
- end
134
- end
135
-
136
- test 'forgotten.sent_email flash' do
137
- user_factory 'Bob', 'bob', 'secret', 'bob@example.com'
138
- submit_forgotten_details 'bob'
139
- within '.flash.notice' do
140
- assert page.has_content?("We've emailed you a link where you can change your password.")
141
- end
142
- end
143
-
144
- test 'forgotten.sent_email flash is optional' do
145
- begin
146
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:forgotten => {:sent_email => ''}}}}
147
- user_factory 'Bob', 'bob', 'secret', 'bob@example.com'
148
- submit_forgotten_details 'bob'
149
- assert page.has_no_css?('div.flash')
150
- ensure
151
- I18n.reload!
152
- end
153
- end
154
-
155
- test 'forgotten.invalid_token flash' do
156
- visit change_password_path('123')
157
- within '.flash.alert' do
158
- assert page.has_content?("Sorry, this link isn't valid anymore.")
159
- end
160
- end
161
-
162
- test 'forgotten.invalid_token flash is optional' do
163
- begin
164
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:forgotten => {:invalid_token => ''}}}}
165
- visit change_password_path('123')
166
- assert page.has_no_css?('div.flash')
167
- ensure
168
- I18n.reload!
169
- end
170
- end
171
-
172
- test 'forgotten.password_changed flash' do
173
- user_factory 'Bob', 'bob', 'secret', 'bob@example.com'
174
- User.last.generate_token!
175
- visit change_password_path(User.last.token)
176
- fill_in :password, :with => 'topsecret'
177
- click_button 'Change my password'
178
- within '.flash.notice' do
179
- assert page.has_content?("You have successfully changed your password and you're now signed in.")
180
- end
181
- end
182
-
183
- test 'forgotten.password_changed flash is optional' do
184
- begin
185
- I18n.backend.store_translations :en, {:quo_vadis => {:flash => {:forgotten => {:password_changed => ''}}}}
186
- user_factory 'Bob', 'bob', 'secret', 'bob@example.com'
187
- User.last.generate_token!
188
- visit change_password_path(User.last.token)
189
- fill_in :password, :with => 'topsecret'
190
- click_button 'Change my password'
191
- assert page.has_no_css?('div.flash')
192
- ensure
193
- I18n.reload!
194
- end
195
- end
196
-
197
- end
@@ -1,7 +0,0 @@
1
- require 'test_helper'
2
-
3
- class NavigationTest < ActiveSupport::IntegrationCase
4
- test 'Sanity test' do
5
- assert_kind_of Dummy::Application, Rails.application
6
- end
7
- end
@@ -1,26 +0,0 @@
1
- require 'test_helper'
2
-
3
- class SignInPersonTest < ActiveSupport::IntegrationCase
4
-
5
- # NOTE: it would be great if I could figure out how to re-initialise the method's
6
- # mixed into the controller with the new model.
7
- test 'successful sign in for a non-user model' do
8
- puts <<-END
9
- NOTE: this test (#{__FILE__}) has to be run individually like this:
10
-
11
- 1. Change lib/quo_vadis.rb's @@model to 'Person'.
12
- 2. Uncomment the test code.
13
- 3. bundle exec ruby -Ilib:test #{__FILE__}
14
-
15
- END
16
-
17
- # person_factory 'James', 'jim', 'secret'
18
- # sign_in_as 'jim', 'secret'
19
-
20
- # assert_equal root_path, current_path
21
- # within '.flash.notice' do
22
- # assert page.has_content?('You have successfully signed in.')
23
- # end
24
- end
25
-
26
- end