quo_vadis 1.4.2 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +11 -7
  3. data/CHANGELOG.md +43 -0
  4. data/Gemfile +11 -1
  5. data/LICENSE.txt +21 -0
  6. data/README.md +460 -127
  7. data/Rakefile +15 -9
  8. data/app/controllers/quo_vadis/confirmations_controller.rb +102 -0
  9. data/app/controllers/quo_vadis/logs_controller.rb +20 -0
  10. data/app/controllers/quo_vadis/password_resets_controller.rb +68 -0
  11. data/app/controllers/quo_vadis/passwords_controller.rb +28 -0
  12. data/app/controllers/quo_vadis/recovery_codes_controller.rb +54 -0
  13. data/app/controllers/quo_vadis/sessions_controller.rb +50 -132
  14. data/app/controllers/quo_vadis/totps_controller.rb +72 -0
  15. data/app/controllers/quo_vadis/twofas_controller.rb +26 -0
  16. data/app/mailers/quo_vadis/mailer.rb +73 -0
  17. data/app/models/quo_vadis/account.rb +72 -0
  18. data/app/models/quo_vadis/account_confirmation_token.rb +17 -0
  19. data/app/models/quo_vadis/log.rb +59 -0
  20. data/app/models/quo_vadis/password.rb +68 -0
  21. data/app/models/quo_vadis/password_reset_token.rb +17 -0
  22. data/app/models/quo_vadis/recovery_code.rb +26 -0
  23. data/app/models/quo_vadis/session.rb +55 -0
  24. data/app/models/quo_vadis/token.rb +42 -0
  25. data/app/models/quo_vadis/totp.rb +56 -0
  26. data/app/views/quo_vadis/confirmations/edit.html.erb +10 -0
  27. data/app/views/quo_vadis/confirmations/edit_email.html.erb +14 -0
  28. data/app/views/quo_vadis/confirmations/index.html.erb +14 -0
  29. data/app/views/quo_vadis/confirmations/new.html.erb +16 -0
  30. data/app/views/quo_vadis/logs/index.html.erb +30 -0
  31. data/app/views/quo_vadis/mailer/account_confirmation.text.erb +4 -0
  32. data/app/views/quo_vadis/mailer/email_change_notification.text.erb +8 -0
  33. data/app/views/quo_vadis/mailer/identifier_change_notification.text.erb +8 -0
  34. data/app/views/quo_vadis/mailer/password_change_notification.text.erb +8 -0
  35. data/app/views/quo_vadis/mailer/password_reset_notification.text.erb +8 -0
  36. data/app/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb +8 -0
  37. data/app/views/quo_vadis/mailer/reset_password.text.erb +4 -0
  38. data/app/views/quo_vadis/mailer/totp_reuse_notification.text.erb +6 -0
  39. data/app/views/quo_vadis/mailer/totp_setup_notification.text.erb +8 -0
  40. data/app/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb +8 -0
  41. data/app/views/quo_vadis/password_resets/edit.html.erb +17 -0
  42. data/app/views/quo_vadis/password_resets/index.html.erb +5 -0
  43. data/app/views/quo_vadis/password_resets/new.html.erb +12 -0
  44. data/app/views/quo_vadis/passwords/edit.html.erb +22 -0
  45. data/app/views/quo_vadis/recovery_codes/challenge.html.erb +11 -0
  46. data/app/views/quo_vadis/recovery_codes/index.html.erb +25 -0
  47. data/app/views/quo_vadis/sessions/index.html.erb +26 -0
  48. data/app/views/quo_vadis/sessions/new.html.erb +24 -0
  49. data/app/views/quo_vadis/totps/challenge.html.erb +11 -0
  50. data/app/views/quo_vadis/totps/new.html.erb +17 -0
  51. data/app/views/quo_vadis/twofas/show.html.erb +20 -0
  52. data/bin/console +15 -0
  53. data/bin/rails +21 -0
  54. data/bin/setup +8 -0
  55. data/config/locales/quo_vadis.en.yml +80 -24
  56. data/config/routes.rb +46 -12
  57. data/db/migrate/202102150904_setup.rb +48 -0
  58. data/lib/generators/quo_vadis/install_generator.rb +4 -23
  59. data/lib/quo_vadis.rb +103 -97
  60. data/lib/quo_vadis/controller.rb +227 -0
  61. data/lib/quo_vadis/crypt.rb +43 -0
  62. data/lib/quo_vadis/current_request_details.rb +11 -0
  63. data/lib/quo_vadis/defaults.rb +18 -0
  64. data/lib/quo_vadis/encrypted_type.rb +17 -0
  65. data/lib/quo_vadis/engine.rb +9 -11
  66. data/lib/quo_vadis/hmacable.rb +26 -0
  67. data/lib/quo_vadis/ip_masking.rb +31 -0
  68. data/lib/quo_vadis/model.rb +83 -0
  69. data/lib/quo_vadis/version.rb +3 -1
  70. data/quo_vadis.gemspec +20 -25
  71. data/test/dummy/README.markdown +1 -0
  72. data/test/dummy/Rakefile +2 -6
  73. data/test/dummy/app/controllers/application_controller.rb +0 -1
  74. data/test/dummy/app/controllers/articles_controller.rb +8 -11
  75. data/test/dummy/app/controllers/sign_ups_controller.rb +42 -0
  76. data/test/dummy/app/controllers/users_controller.rb +13 -5
  77. data/test/dummy/app/models/application_record.rb +3 -0
  78. data/test/dummy/app/models/article.rb +2 -1
  79. data/test/dummy/app/models/person.rb +5 -2
  80. data/test/dummy/app/models/user.rb +4 -1
  81. data/test/dummy/app/views/articles/also_secret.html.erb +1 -0
  82. data/test/dummy/app/views/articles/index.html.erb +1 -1
  83. data/test/dummy/app/views/articles/secret.html.erb +1 -0
  84. data/test/dummy/app/views/articles/very_secret.html.erb +2 -0
  85. data/test/dummy/app/views/layouts/application.html.erb +41 -25
  86. data/test/dummy/app/views/sign_ups/new.html.erb +37 -0
  87. data/test/dummy/app/views/sign_ups/show.html.erb +5 -0
  88. data/test/dummy/app/views/users/new.html.erb +32 -9
  89. data/test/dummy/config.ru +6 -3
  90. data/test/dummy/config/application.rb +18 -9
  91. data/test/dummy/config/boot.rb +3 -9
  92. data/test/dummy/config/database.yml +2 -14
  93. data/test/dummy/config/environment.rb +2 -3
  94. data/test/dummy/config/initializers/quo_vadis.rb +2 -76
  95. data/test/dummy/config/routes.rb +11 -3
  96. data/test/dummy/db/migrate/202102121932_create_users.rb +10 -0
  97. data/test/dummy/db/migrate/202102121935_create_people.rb +10 -0
  98. data/test/dummy/db/schema.rb +80 -21
  99. data/test/fixtures/quo_vadis/mailer/account_confirmation.text +4 -0
  100. data/test/fixtures/quo_vadis/mailer/email_change_notification.text +8 -0
  101. data/test/fixtures/quo_vadis/mailer/identifier_change_notification.text +8 -0
  102. data/test/fixtures/quo_vadis/mailer/password_change_notification.text +8 -0
  103. data/test/fixtures/quo_vadis/mailer/password_reset_notification.text +8 -0
  104. data/test/fixtures/quo_vadis/mailer/recovery_codes_generation_notification.text +8 -0
  105. data/test/fixtures/quo_vadis/mailer/reset_password.text +4 -0
  106. data/test/fixtures/quo_vadis/mailer/totp_reuse_notification.text +6 -0
  107. data/test/fixtures/quo_vadis/mailer/totp_setup_notification.text +8 -0
  108. data/test/fixtures/quo_vadis/mailer/twofa_deactivated_notification.text +8 -0
  109. data/test/integration/account_confirmation_test.rb +145 -0
  110. data/test/integration/controller_test.rb +280 -0
  111. data/test/integration/logging_test.rb +244 -0
  112. data/test/integration/password_change_test.rb +99 -0
  113. data/test/integration/password_login_test.rb +137 -0
  114. data/test/integration/password_reset_test.rb +136 -0
  115. data/test/integration/recovery_codes_test.rb +48 -0
  116. data/test/integration/sessions_test.rb +86 -0
  117. data/test/integration/sign_up_test.rb +26 -12
  118. data/test/integration/totps_test.rb +96 -0
  119. data/test/integration/twofa_test.rb +82 -0
  120. data/test/mailers/mailer_test.rb +200 -0
  121. data/test/models/account_test.rb +50 -0
  122. data/test/models/crypt_test.rb +22 -0
  123. data/test/models/log_test.rb +16 -0
  124. data/test/models/mask_ip_test.rb +27 -0
  125. data/test/models/model_test.rb +66 -0
  126. data/test/models/password_test.rb +179 -0
  127. data/test/models/recovery_code_test.rb +54 -0
  128. data/test/models/session_test.rb +67 -0
  129. data/test/models/token_test.rb +70 -0
  130. data/test/models/totp_test.rb +68 -0
  131. data/test/quo_vadis_test.rb +39 -3
  132. data/test/test_helper.rb +42 -70
  133. metadata +120 -204
  134. data/app/controllers/controller_mixin.rb +0 -109
  135. data/app/mailers/quo_vadis/notifier.rb +0 -30
  136. data/app/models/model_mixin.rb +0 -128
  137. data/lib/generators/quo_vadis/templates/migration.rb.erb +0 -18
  138. data/lib/generators/quo_vadis/templates/quo_vadis.rb.erb +0 -96
  139. data/test/dummy/.gitignore +0 -2
  140. data/test/dummy/app/helpers/application_helper.rb +0 -2
  141. data/test/dummy/app/helpers/articles_helper.rb +0 -2
  142. data/test/dummy/app/views/articles/new.html.erb +0 -11
  143. data/test/dummy/app/views/layouts/sessions.html.erb +0 -3
  144. data/test/dummy/app/views/quo_vadis/notifier/change_password.text.erb +0 -9
  145. data/test/dummy/app/views/quo_vadis/notifier/invite.text.erb +0 -8
  146. data/test/dummy/app/views/sessions/edit.html.erb +0 -11
  147. data/test/dummy/app/views/sessions/forgotten.html.erb +0 -13
  148. data/test/dummy/app/views/sessions/invite.html.erb +0 -31
  149. data/test/dummy/app/views/sessions/new.html.erb +0 -15
  150. data/test/dummy/config/environments/development.rb +0 -26
  151. data/test/dummy/config/environments/production.rb +0 -49
  152. data/test/dummy/config/environments/test.rb +0 -37
  153. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  154. data/test/dummy/config/initializers/inflections.rb +0 -10
  155. data/test/dummy/config/initializers/mime_types.rb +0 -5
  156. data/test/dummy/config/initializers/rack_patch.rb +0 -16
  157. data/test/dummy/config/initializers/secret_token.rb +0 -7
  158. data/test/dummy/config/initializers/session_store.rb +0 -8
  159. data/test/dummy/config/locales/en.yml +0 -5
  160. data/test/dummy/config/locales/quo_vadis.en.yml +0 -21
  161. data/test/dummy/db/migrate/20110124125037_create_users.rb +0 -13
  162. data/test/dummy/db/migrate/20110124131535_create_articles.rb +0 -14
  163. data/test/dummy/db/migrate/20110127094709_add_authentication_to_users.rb +0 -18
  164. data/test/dummy/db/migrate/20111004112209_create_people.rb +0 -13
  165. data/test/dummy/db/migrate/20111004132342_add_authentication_to_people.rb +0 -18
  166. data/test/dummy/public/404.html +0 -26
  167. data/test/dummy/public/422.html +0 -26
  168. data/test/dummy/public/500.html +0 -26
  169. data/test/dummy/public/javascripts/application.js +0 -2
  170. data/test/dummy/public/javascripts/controls.js +0 -965
  171. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  172. data/test/dummy/public/javascripts/effects.js +0 -1123
  173. data/test/dummy/public/javascripts/prototype.js +0 -6001
  174. data/test/dummy/public/javascripts/rails.js +0 -175
  175. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  176. data/test/dummy/script/rails +0 -6
  177. data/test/integration/activation_test.rb +0 -108
  178. data/test/integration/authenticate_test.rb +0 -39
  179. data/test/integration/blocked_test.rb +0 -23
  180. data/test/integration/config_test.rb +0 -118
  181. data/test/integration/cookie_test.rb +0 -67
  182. data/test/integration/csrf_test.rb +0 -41
  183. data/test/integration/forgotten_test.rb +0 -93
  184. data/test/integration/helper_test.rb +0 -18
  185. data/test/integration/locale_test.rb +0 -197
  186. data/test/integration/navigation_test.rb +0 -7
  187. data/test/integration/sign_in_person_test.rb +0 -26
  188. data/test/integration/sign_in_test.rb +0 -24
  189. data/test/integration/sign_out_test.rb +0 -20
  190. data/test/support/integration_case.rb +0 -11
  191. data/test/unit/user_test.rb +0 -75
@@ -0,0 +1,11 @@
1
+ <h1>2FA</h1>
2
+
3
+ <p>Enter the 6 digit code generated by your authenticator:</p>
4
+
5
+ <%= form_with url: authenticate_totps_path, method: :post do |f| %>
6
+ <%= f.text_field :totp, inputmode: 'decimal', autocomplete: 'one-time-code', autofocus: true, maxlength: '6' %>
7
+
8
+ <%= f.submit 'Verify' %>
9
+ <% end %>
10
+
11
+ <p><%= link_to 'I want to use a recovery code instead.', quo_vadis.challenge_recovery_codes_path %></p>
@@ -0,0 +1,17 @@
1
+ <h1>2FA: TOTP setup</h1>
2
+
3
+ <p>1. With your authenticator app, either scan this QR code or enter the text: <pre><%= @totp.key %></pre></p>
4
+
5
+ <%== @totp.qr_code.as_svg module_size: 5 %>
6
+
7
+
8
+ <p>2. Enter the 6 digit code generated by your authenticator, to check it worked:</p>
9
+
10
+ <%= form_with model: @totp do |f| %>
11
+ <%= f.hidden_field :key %>
12
+ <%= f.hidden_field :hmac_key %>
13
+
14
+ <%= f.text_field :otp, inputmode: 'decimal', autocomplete: 'one-time-code', autofocus: true, maxlength: '6' %>
15
+
16
+ <%= f.submit 'Confirm' %>
17
+ <% end %>
@@ -0,0 +1,20 @@
1
+ <h1>2FA</h1>
2
+
3
+ <% if authenticated_model.qv_account.has_two_factors? %>
4
+
5
+ <p>You have set up 2FA.</p>
6
+ <p><%= button_to 'Deactivate your 2FA credentials', twofa_path, method: :delete %></p>
7
+
8
+ <% else %>
9
+
10
+ <p>You do not have 2FA set up.</p>
11
+ <% if QuoVadis.two_factor_authentication_mandatory %>
12
+ <p>Please <%= link_to 'set it up', new_totp_path %> now.</p>
13
+ <% else %>
14
+ <p>You can <%= link_to 'set it up', new_totp_path %> if you like.</p>
15
+ <% end %>
16
+
17
+ <% end %>
18
+
19
+
20
+ <p> You have <%= link_to pluralize(@recovery_codes_count, 'recovery code'), recovery_codes_path %> left.</p>
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "quo_vadis"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/rails ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('..', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/quo_vadis/engine', __dir__)
7
+ APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
11
+ require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
12
+
13
+ require "active_model/railtie"
14
+ require "active_record/railtie"
15
+ # require "action_controller/railtie"
16
+ # require "action_view/railtie"
17
+ # require "action_mailer/railtie"
18
+ require "rails/test_unit/railtie"
19
+
20
+ # require "rails/all"
21
+ require "rails/engine/commands"
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -1,28 +1,84 @@
1
1
  en:
2
2
  quo_vadis:
3
3
  flash:
4
- sign_in:
5
- before: 'Please sign in first.'
6
- after: 'You have successfully signed in.'
7
- failed: 'Sorry, we did not recognise you.'
8
- blocked: 'Sorry, your account is blocked.'
9
-
10
- sign_out: 'You have successfully signed out.'
11
-
12
- forgotten:
13
- unknown: "Sorry, we did not recognise you."
14
- no_email: "Sorry, we don't have an email address for you."
15
- sent_email: "We've emailed you a link where you can change your password."
16
- invalid_token: "Sorry, this link isn't valid anymore."
17
- password_changed: "You have successfully changed your password and you're now signed in."
18
-
19
- activation:
20
- accepted: "Your account is active and you're now signed in."
21
- invalid_token: "Sorry, this link isn't valid anymore."
22
- invalid_credentials: "Sorry, we couldn't accept that username and/or password."
23
-
24
- notifier:
25
- change_password:
4
+ login:
5
+ failed: Sorry, we did not recognise you.
6
+ success: Welcome back!
7
+ logout:
8
+ self: You have logged out.
9
+ other: You have logged out of the other session.
10
+ require_authentication: Please log in first.
11
+ password:
12
+ update: Your password has been changed.
13
+ password_reset:
14
+ create: A link to change your password has been emailed to you.
15
+ unknown: Either the link has expired or you have already reset your password.
16
+ reset: Your password has been changed and you are logged in.
17
+ confirmation:
18
+ create: A link to confirm your account has been emailed to you.
19
+ required: Please confirm your account first.
20
+ identifier: Sorry, your account could not be found. Please try again.
21
+ unknown: Either the link has expired or your account has already been confirmed.
22
+ confirmed: Thanks for confirming your account. You are now logged in.
23
+ totp:
24
+ unverified: Sorry, the code was incorrect. Please check your system clock is correct and try again.
25
+ setup: Please set up two factor authentication.
26
+ recovery_code:
27
+ unverified: Sorry, the code was incorrect. Please try again (you cannot reuse a code you have used before).
28
+ success:
29
+ zero: You have used up all your recovery codes now. Please generate a new set.
30
+ one: You have one recovery code left.
31
+ other: You have %{count} recovery codes left.
32
+ 2fa:
33
+ invalidated: You have invalidated your 2FA credentials and recovery codes.
34
+ mailer:
35
+ password_reset:
26
36
  subject: Change your password
27
- invite:
28
- subject: Activate your account
37
+ confirmation:
38
+ subject: Please confirm your account
39
+ notification:
40
+ email_change: Your email address has been changed
41
+ identifier_change: Your %{identifier} has been changed
42
+ password_change: Your password has been changed
43
+ password_reset: Your password has been reset
44
+ totp_setup: Two-factor authentication was set up just now
45
+ totp_reuse: Your two-factor authentication code was reused just now
46
+ twofa_deactivated: Two-factor authentication was deactivated just now
47
+ recovery_codes_generation: Recovery codes have been generated for your account
48
+ log:
49
+ action:
50
+ login:
51
+ success: Logged in
52
+ failure: Failed login attempt (incorrect password)
53
+ unknown: Failed login attempt (unknown identifier)
54
+ totp:
55
+ setup: TOTP set up for 2FA
56
+ success: Authenticated via TOTP
57
+ failure: Failed authentication attempt via TOTP
58
+ reuse: Failed attempt to reuse TOTP code
59
+ recovery_code:
60
+ success: Authenticated via 2FA recovery code
61
+ failure: Failed authentication attempt via 2FA recovery code
62
+ generate: Generated new 2FA recovery codes
63
+ 2fa:
64
+ deactivated: Deactivated 2FA
65
+ identifier:
66
+ change: Changed identifier
67
+ email:
68
+ change: Changed email address
69
+ password:
70
+ change: Changed password
71
+ reset: Reset password
72
+ account:
73
+ confirmation: Confirmed account
74
+ logout:
75
+ self: Logged out
76
+ other: Logged out session remotely
77
+ revoke: Revoked access
78
+ activerecord:
79
+ errors:
80
+ models:
81
+ quo_vadis/password:
82
+ attributes:
83
+ password:
84
+ incorrect: is incorrect
data/config/routes.rb CHANGED
@@ -1,15 +1,49 @@
1
- Rails.application.routes.draw do
2
- scope :module => 'quo_vadis' do
3
- get 'sign-in' => 'sessions#new', :as => 'sign_in'
4
- post 'sign-in' => 'sessions#create', :as => 'sign_in'
5
- get 'sign-out' => 'sessions#destroy', :as => 'sign_out'
6
- get 'sign-in/forgotten' => 'sessions#forgotten', :as => 'forgotten_sign_in'
7
- post 'sign-in/forgotten' => 'sessions#forgotten', :as => 'forgotten_sign_in'
8
- constraints :token => /.+/ do
9
- get 'sign-in/change-password/:token' => 'sessions#edit', :as => 'change_password'
10
- put 'sign-in/change-password/:token' => 'sessions#update', :as => 'change_password'
11
- get 'sign-in/invite/:token' => 'sessions#invite', :as => 'invitation'
12
- post 'sign-in/accept/:token' => 'sessions#accept', :as => 'activation'
1
+ # frozen_string_literal: true
2
+
3
+ QuoVadis::Engine.routes.draw do
4
+ get 'login', to: 'sessions#new'
5
+ post 'login', to: 'sessions#create'
6
+ delete 'logout', to: 'sessions#destroy'
7
+
8
+ resources :logs, only: :index
9
+
10
+ resources :sessions, only: [:index, :destroy]
11
+
12
+ resource :password, only: [:edit, :update]
13
+
14
+ resources :password_resets, only: [:new, :create, :index]
15
+ get '/pwd-reset/:token', to: 'password_resets#edit', as: 'password_reset'
16
+ put '/pwd-reset/:token', to: 'password_resets#update'
17
+
18
+ resources :confirmations, only: [:new, :create, :index] do
19
+ collection do
20
+ get :edit_email
21
+ put :update_email
22
+ post :resend
23
+ end
24
+ end
25
+ get '/confirm/:token', to: 'confirmations#edit', as: 'confirmation'
26
+ put '/confirm/:token', to: 'confirmations#update'
27
+
28
+ resources :totps, only: [:new, :create] do
29
+ collection do
30
+ get :challenge
31
+ post :authenticate
13
32
  end
14
33
  end
34
+
35
+ resources :recovery_codes, only: [:index] do
36
+ collection do
37
+ get :challenge
38
+ post :authenticate
39
+ post :generate
40
+ end
41
+ end
42
+
43
+ resource :twofa, path: '2fa'
44
+ end
45
+
46
+
47
+ Rails.application.routes.draw do
48
+ mount QuoVadis::Engine, at: QuoVadis.mount_point, as: :quo_vadis
15
49
  end
@@ -0,0 +1,48 @@
1
+ class Setup < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :qv_accounts do |t|
4
+ t.references :model, polymorphic: true, index: {unique: true}, null: false
5
+ t.string :identifier, index: {unique: true}, null: false
6
+ t.datetime :confirmed_at
7
+ t.timestamps
8
+ end
9
+
10
+ create_table :qv_passwords do |t|
11
+ t.references :account, foreign_key: {to_table: :qv_accounts}, null: false
12
+ t.string :password_digest, null: false
13
+ t.timestamps
14
+ end
15
+
16
+ create_table :qv_sessions do |t|
17
+ t.references :account, foreign_key: {to_table: :qv_accounts}, null: false
18
+ t.string :ip, null: false
19
+ t.string :user_agent, null: false
20
+ t.datetime :lifetime_expires_at
21
+ t.datetime :last_seen_at
22
+ t.datetime :second_factor_at
23
+ t.timestamps
24
+ end
25
+
26
+ create_table :qv_totps do |t|
27
+ t.references :account, foreign_key: {to_table: :qv_accounts}, null: false
28
+ t.string :key, null: false
29
+ t.integer :last_used_at, null: false
30
+ t.timestamps
31
+ end
32
+
33
+ create_table :qv_recovery_codes do |t|
34
+ t.references :account, foreign_key: {to_table: :qv_accounts}, null: false
35
+ t.string :code_digest, null: false
36
+ t.timestamps
37
+ end
38
+
39
+ create_table :qv_logs do |t|
40
+ t.references :account, foreign_key: {to_table: :qv_accounts}
41
+ t.string :action, null: false
42
+ t.string :ip, null: false
43
+ t.json :metadata, null: false, default: {}
44
+ t.timestamps
45
+ end
46
+ end
47
+ end
48
+
@@ -1,29 +1,10 @@
1
- require 'rails/generators'
2
- require 'rails/generators/migration'
3
- require 'rails/generators/active_record/migration'
4
-
5
1
  module QuoVadis
6
2
  class InstallGenerator < Rails::Generators::Base
7
- include Rails::Generators::Migration
8
- extend ActiveRecord::Generators::Migration
9
-
10
- source_root File.expand_path('../templates', __FILE__)
11
- argument :model_name, :type => :string, :default => 'User'
12
-
13
- desc 'Copies an initializer, a locale file, and a migration to your application.'
3
+ source_root Pathname.new(__dir__) / '..' / '..' / '..' / 'app' / 'views' / 'quo_vadis'
14
4
 
15
-
16
- def copy_locale_file
17
- copy_file '../../../../config/locales/quo_vadis.en.yml', 'config/locales/quo_vadis.en.yml'
5
+ desc "Copy QuoVadis' views into your app."
6
+ def copy_views
7
+ directory '.', Pathname.new('app') / 'views' / 'quo_vadis'
18
8
  end
19
-
20
- def copy_initializer_file
21
- template 'quo_vadis.rb.erb', 'config/initializers/quo_vadis.rb'
22
- end
23
-
24
- def create_migration_file
25
- migration_template 'migration.rb.erb', "db/migrate/add_authentication_to_#{model_name.tableize}.rb"
26
- end
27
-
28
9
  end
29
10
  end
data/lib/quo_vadis.rb CHANGED
@@ -1,119 +1,125 @@
1
- require 'quo_vadis/engine'
2
- require 'active_support/core_ext/numeric/time'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module QuoVadis
5
4
 
6
- # The model we want to authenticate.
7
- mattr_accessor :model # :nodoc:
8
- @@model = 'User'
9
-
10
- def self.model_class # :nodoc
11
- @@model.constantize
12
- end
13
-
14
- def self.model_instance_name # :nodoc
15
- @@model.tableize.singularize # e.g. 'user'
16
- end
17
-
18
-
19
- #
20
- # Sign in
21
- #
22
-
23
- # The URL to redirect the user to after s/he signs in.
24
- mattr_accessor :signed_in_url
25
- @@signed_in_url = :root
26
-
27
- # Whether the `:signed_in_url` should override the URL the user was trying
28
- # to reach when they were made to authenticate.
29
- mattr_accessor :override_original_url
30
- @@override_original_url = false
31
-
32
- def self.signed_in_url(user, original_url, controller) # :nodoc:
33
- if original_url && !@@override_original_url
34
- original_url
35
- else
36
- @@signed_in_url.respond_to?(:call) ? @@signed_in_url.call(user, controller) : @@signed_in_url
5
+ Error = Class.new StandardError
6
+ PasswordExistsError = Class.new QuoVadis::Error
7
+
8
+ def self.accessor(*names)
9
+ names.each do |name|
10
+ define_singleton_method name do |val = nil|
11
+ if !val.nil?
12
+ instance_variable_set :"@#{name}", val
13
+ else
14
+ instance_variable_get :"@#{name}"
15
+ end
16
+ end
37
17
  end
38
18
  end
39
19
 
40
- # Code to run when the user has signed in.
41
- mattr_accessor :signed_in_hook
42
- @@signed_in_hook = nil
43
-
44
- def self.signed_in_hook(user, controller) # :nodoc:
45
- @@signed_in_hook.call(user, controller) if @@signed_in_hook
46
- end
47
-
48
- # Code to run when someone has tried but failed to sign in.
49
- mattr_accessor :failed_sign_in_hook
50
- @@failed_sign_in_hook = nil
51
-
52
- def self.failed_sign_in_hook(controller) # :nodoc:
53
- @@failed_sign_in_hook.call(controller) if @@failed_sign_in_hook
54
- end
20
+ accessor \
21
+ :password_minimum_length, # integer
22
+ :mask_ips, # true | false
23
+ :cookie_name, # string
24
+ :session_lifetime, # :session | ActiveSupport::Duration | [integer] seconds
25
+ :session_lifetime_extend_to_end_of_day,# true | false
26
+ :session_idle_timeout, # :lifetime | ActiveSupport::Duration | [integer] seconds
27
+ :password_reset_token_lifetime, # ActiveSupport::Duration | [integer] seconds
28
+ :mail_headers, # hash
29
+ :accounts_require_confirmation, # true | false
30
+ :account_confirmation_token_lifetime, # ActiveSupport::Duration | [integer] seconds
31
+ :enqueue_transactional_emails, # true | false
32
+ :app_name, # string
33
+ :two_factor_authentication_mandatory, # true | false
34
+ :mount_point # string
35
+
36
+ class << self
37
+ def configure(&block)
38
+ module_eval &block
39
+ end
55
40
 
56
- # How long to remember user across browser sessions.
57
- mattr_accessor :remember_for
58
- @@remember_for = 2.weeks
41
+ # name - string class name, e.g. 'User'
42
+ # identifier - attribute symbol, e.g. :username
43
+ def register_model(name, identifier)
44
+ models[name] = identifier
45
+ end
59
46
 
60
- # The domain to use for remember-me cookies.
61
- mattr_accessor :cookie_domain
62
- @@cookie_domain = :all
47
+ def find_account_by_identifier_in_params(params)
48
+ Account.find_by identifier: identifier_value_in_params(params)
49
+ end
63
50
 
51
+ def identifier_value_in_params(params)
52
+ identifier = detect_identifier params.keys
53
+ params[identifier]
54
+ end
64
55
 
65
- # Whether the sign-in process is blocked to the user.
66
- mattr_writer :blocked
67
- @@blocked = false
56
+ # model - string class name, e.g. 'User'
57
+ # returns string humanised name for the model's identifier, e.g. "Username"
58
+ def humanise_identifier(model)
59
+ klass = model.constantize
60
+ klass.human_attribute_name identifier(model)
61
+ end
68
62
 
69
- def self.blocked?(controller) # :nodoc:
70
- @@blocked.respond_to?(:call) ? @@blocked.call(controller) : @@blocked
71
- end
63
+ # Translates the key in the :quo_vadis scope, returning nil if it does not exist.
64
+ # This allows applications to suppress a QuoVadis translation.
65
+ # For example:
66
+ #
67
+ # en:
68
+ # quo_vadis:
69
+ # require_authentication:
70
+ #
71
+ def translate(key, **options)
72
+ I18n.t key, **options.merge(scope: :quo_vadis, raise: true) rescue nil
73
+ end
72
74
 
75
+ def notify(action, params)
76
+ QuoVadis::Mailer.with(params).send(action).deliver_later
77
+ end
73
78
 
74
- #
75
- # Sign out
76
- #
79
+ def deliver(action, params)
80
+ mail = QuoVadis::Mailer.with(params).send(action)
81
+ QuoVadis.enqueue_transactional_emails ?
82
+ mail.deliver_later :
83
+ mail.deliver_now
84
+ end
77
85
 
78
- # The URL to redirect the user to after s/he signs out.
79
- mattr_accessor :signed_out_url
80
- @@signed_out_url = :root
86
+ # model - string class name, e.g. 'User'
87
+ # returns attribute symbol, e.g. :username
88
+ def identifier(model)
89
+ models[model]
90
+ end
81
91
 
82
- def self.signed_out_url(controller) # :nodoc:
83
- @@signed_out_url.respond_to?(:call) ? @@signed_out_url.call(controller) : @@signed_out_url
84
- end
92
+ private
85
93
 
94
+ def models
95
+ @models ||= {}
96
+ end
86
97
 
87
- # Code to run just before the user has signed out.
88
- mattr_accessor :signed_out_hook
89
- @@signed_out_hook = nil
98
+ def detect_identifier(candidates)
99
+ (identifiers.map(&:to_s) & candidates.map(&:to_s)).first
100
+ end
90
101
 
91
- def self.signed_out_hook(user, controller) # :nodoc:
92
- @@signed_out_hook.call(user, controller) if @@signed_out_hook
102
+ def identifiers
103
+ models.values.uniq
104
+ end
93
105
  end
106
+ end
94
107
 
108
+ require_relative 'quo_vadis/defaults'
109
+ require_relative 'quo_vadis/engine'
110
+ require_relative 'quo_vadis/crypt'
111
+ require_relative 'quo_vadis/encrypted_type'
112
+ require_relative 'quo_vadis/hmacable'
113
+ require_relative 'quo_vadis/ip_masking'
114
+ require_relative 'quo_vadis/model'
115
+ require_relative 'quo_vadis/current_request_details'
116
+ require_relative 'quo_vadis/controller'
117
+
118
+ ActiveSupport.on_load(:action_controller) do
119
+ include QuoVadis::Controller
120
+ end
95
121
 
96
- #
97
- # Forgotten-password and activation Mailer
98
- #
99
-
100
- # From whom the forgotten-password email should be sent.
101
- mattr_accessor :from
102
- @@from = 'noreply@example.com'
103
-
104
-
105
- #
106
- # Miscellaneous
107
- #
108
-
109
- # Layout for the sign-in view.
110
- mattr_accessor :layout
111
- @@layout = nil
112
-
113
-
114
- # Configure from the initializer.
115
- def self.configure
116
- yield self
117
- end
118
-
122
+ ActiveSupport.on_load(:active_record) do
123
+ include QuoVadis::Model
119
124
  end
125
+