quo_vadis 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +7 -0
  4. data/Gemfile +0 -3
  5. data/README.md +4 -5
  6. data/lib/quo_vadis/version.rb +1 -1
  7. data/quo_vadis.gemspec +5 -3
  8. data/test/dummy/README.markdown +1 -0
  9. data/test/dummy/Rakefile +3 -0
  10. data/test/dummy/app/controllers/application_controller.rb +2 -0
  11. data/test/dummy/app/controllers/articles_controller.rb +17 -0
  12. data/test/dummy/app/controllers/sign_ups_controller.rb +42 -0
  13. data/test/dummy/app/controllers/users_controller.rb +25 -0
  14. data/test/dummy/app/models/application_record.rb +3 -0
  15. data/test/dummy/app/models/article.rb +3 -0
  16. data/test/dummy/app/models/person.rb +6 -0
  17. data/test/dummy/app/models/user.rb +6 -0
  18. data/test/dummy/app/views/articles/also_secret.html.erb +1 -0
  19. data/test/dummy/app/views/articles/index.html.erb +1 -0
  20. data/test/dummy/app/views/articles/secret.html.erb +1 -0
  21. data/test/dummy/app/views/articles/very_secret.html.erb +2 -0
  22. data/test/dummy/app/views/layouts/application.html.erb +46 -0
  23. data/test/dummy/app/views/quo_vadis/confirmations/edit.html.erb +10 -0
  24. data/test/dummy/app/views/quo_vadis/confirmations/index.html.erb +5 -0
  25. data/test/dummy/app/views/quo_vadis/confirmations/new.html.erb +16 -0
  26. data/test/dummy/app/views/quo_vadis/logs/index.html.erb +28 -0
  27. data/test/dummy/app/views/quo_vadis/mailer/account_confirmation.text.erb +4 -0
  28. data/test/dummy/app/views/quo_vadis/mailer/email_change_notification.text.erb +8 -0
  29. data/test/dummy/app/views/quo_vadis/mailer/identifier_change_notification.text.erb +8 -0
  30. data/test/dummy/app/views/quo_vadis/mailer/password_change_notification.text.erb +8 -0
  31. data/test/dummy/app/views/quo_vadis/mailer/password_reset_notification.text.erb +8 -0
  32. data/test/dummy/app/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb +8 -0
  33. data/test/dummy/app/views/quo_vadis/mailer/reset_password.text.erb +4 -0
  34. data/test/dummy/app/views/quo_vadis/mailer/totp_reuse_notification.text.erb +6 -0
  35. data/test/dummy/app/views/quo_vadis/mailer/totp_setup_notification.text.erb +8 -0
  36. data/test/dummy/app/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb +8 -0
  37. data/test/dummy/app/views/quo_vadis/password_resets/edit.html.erb +25 -0
  38. data/test/dummy/app/views/quo_vadis/password_resets/index.html.erb +5 -0
  39. data/test/dummy/app/views/quo_vadis/password_resets/new.html.erb +12 -0
  40. data/test/dummy/app/views/quo_vadis/passwords/edit.html.erb +30 -0
  41. data/test/dummy/app/views/quo_vadis/recovery_codes/challenge.html.erb +11 -0
  42. data/test/dummy/app/views/quo_vadis/recovery_codes/index.html.erb +25 -0
  43. data/test/dummy/app/views/quo_vadis/sessions/index.html.erb +26 -0
  44. data/test/dummy/app/views/quo_vadis/sessions/new.html.erb +24 -0
  45. data/test/dummy/app/views/quo_vadis/totps/challenge.html.erb +11 -0
  46. data/test/dummy/app/views/quo_vadis/totps/new.html.erb +17 -0
  47. data/test/dummy/app/views/quo_vadis/twofas/show.html.erb +20 -0
  48. data/test/dummy/app/views/sign_ups/new.html.erb +37 -0
  49. data/test/dummy/app/views/sign_ups/show.html.erb +5 -0
  50. data/test/dummy/app/views/users/new.html.erb +37 -0
  51. data/test/dummy/config.ru +7 -0
  52. data/test/dummy/config/application.rb +30 -0
  53. data/test/dummy/config/boot.rb +4 -0
  54. data/test/dummy/config/database.yml +10 -0
  55. data/test/dummy/config/environment.rb +4 -0
  56. data/test/dummy/config/initializers/quo_vadis.rb +7 -0
  57. data/test/dummy/config/routes.rb +13 -0
  58. data/test/dummy/db/migrate/202102121932_create_users.rb +10 -0
  59. data/test/dummy/db/migrate/202102121935_create_people.rb +10 -0
  60. data/test/dummy/db/schema.rb +92 -0
  61. data/test/dummy/public/favicon.ico +0 -0
  62. data/test/fixtures/quo_vadis/mailer/account_confirmation.text +4 -0
  63. data/test/fixtures/quo_vadis/mailer/email_change_notification.text +8 -0
  64. data/test/fixtures/quo_vadis/mailer/identifier_change_notification.text +8 -0
  65. data/test/fixtures/quo_vadis/mailer/password_change_notification.text +8 -0
  66. data/test/fixtures/quo_vadis/mailer/password_reset_notification.text +8 -0
  67. data/test/fixtures/quo_vadis/mailer/recovery_codes_generation_notification.text +8 -0
  68. data/test/fixtures/quo_vadis/mailer/reset_password.text +4 -0
  69. data/test/fixtures/quo_vadis/mailer/totp_reuse_notification.text +6 -0
  70. data/test/fixtures/quo_vadis/mailer/totp_setup_notification.text +8 -0
  71. data/test/fixtures/quo_vadis/mailer/twofa_deactivated_notification.text +8 -0
  72. data/test/integration/account_confirmation_test.rb +112 -0
  73. data/test/integration/controller_test.rb +280 -0
  74. data/test/integration/logging_test.rb +235 -0
  75. data/test/integration/password_change_test.rb +93 -0
  76. data/test/integration/password_login_test.rb +125 -0
  77. data/test/integration/password_reset_test.rb +136 -0
  78. data/test/integration/recovery_codes_test.rb +48 -0
  79. data/test/integration/sessions_test.rb +86 -0
  80. data/test/integration/sign_up_test.rb +35 -0
  81. data/test/integration/totps_test.rb +96 -0
  82. data/test/integration/twofa_test.rb +82 -0
  83. data/test/mailers/mailer_test.rb +200 -0
  84. data/test/models/account_test.rb +34 -0
  85. data/test/models/crypt_test.rb +22 -0
  86. data/test/models/log_test.rb +16 -0
  87. data/test/models/mask_ip_test.rb +27 -0
  88. data/test/models/model_test.rb +66 -0
  89. data/test/models/password_test.rb +163 -0
  90. data/test/models/recovery_code_test.rb +54 -0
  91. data/test/models/session_test.rb +67 -0
  92. data/test/models/token_test.rb +70 -0
  93. data/test/models/totp_test.rb +68 -0
  94. data/test/quo_vadis_test.rb +43 -0
  95. data/test/test_helper.rb +58 -0
  96. metadata +119 -4
  97. data/Gemfile.lock +0 -178
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db2382851913e7455425f7014c13fe2f88eeb19209077c042507b948bef37426
4
- data.tar.gz: da813baac8751b9cbc998c403b824c40eb7b2bf5e9710cde7c88fef0c0a0255f
3
+ metadata.gz: 9ed3f9c506465f26124edf6207abe8bd40d8a87587d779c9789a882eb3894153
4
+ data.tar.gz: 9fb0294a48aef63132e359c97529e9547d3f6e6425ebcd734b2eff08a385fe2f
5
5
  SHA512:
6
- metadata.gz: 837432b39ed54b1150d6982bf0385a923a908fe089a8c20b8753a256764d4d76c47da8588c14a99ae76622508cc7f727d1c4589f9a2f6f9870420310283ac6fd
7
- data.tar.gz: 3c90dbeac8aea06b5eef1cd1afd51097e6228e15a5bdd9984e030d27888ff63fdb61721b4f40d41fc0dad3a91f9f044052fbecca598795395891581880c7f9dd
6
+ metadata.gz: a40cb588843f7e78d186e3203ac082e7238ceb21ced56393b26822b353797f214cd4d0bc77242a1d51d4e197a6ca95f2d0b6eebd97a8f730bda48eb2f86c9c3b
7
+ data.tar.gz: 826424470205327161484deff2aa1a031fd7426bf189c493488a9223fbe542b32ea47c89faa6b732fc3fe9d02da6bcd59cd7d31d042b9a30b775e3fae9229c34
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /test/dummy/log/
10
10
  /test/dummy/tmp/
11
11
  /test/dummy/db/*.sqlite3
12
+ Gemfile.lock
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## 2.0.1 (18 May 2021)
5
+
6
+ * Remove Gemfile.lock from repo.
7
+ * Move runtime dependencies into gemspec.
8
+ * Include test files in gem package (so views can be installed).
9
+
10
+
4
11
  ## 2.0.0 (14 May 2021)
5
12
 
6
13
  * Total rewrite from scratch.
data/Gemfile CHANGED
@@ -7,10 +7,7 @@ gemspec
7
7
 
8
8
  gem "rake", "~> 13.0"
9
9
 
10
- gem "rails", ">= 6" # from rodauth-rails
11
10
  gem 'sqlite3'
12
11
  gem 'capybara'
13
- gem 'rotp'
14
- gem 'rqrcode'
15
12
 
16
13
  # gem "minitest", "~> 5.0"
data/README.md CHANGED
@@ -45,8 +45,7 @@ Then run `bundle install`.
45
45
  Next, add the database tables:
46
46
 
47
47
  ```
48
- $ rails quo_vadis:install:migrations
49
- $ rails db:migrate
48
+ rails quo_vadis:install:migrations && rails db:migrate
50
49
  ```
51
50
 
52
51
  All the database tables are prefixed with `qv_`.
@@ -54,7 +53,7 @@ All the database tables are prefixed with `qv_`.
54
53
  Finally, copy the example views across:
55
54
 
56
55
  ```
57
- $ rails generate quo_vadis:install
56
+ rails generate quo_vadis:install
58
57
  ```
59
58
 
60
59
 
@@ -218,7 +217,7 @@ Here's the workflow:
218
217
  4. [Account-confirmation confirmation page] The user clicks a button to confirm their account. (This step is to prevent any link prefetching in the user's mail client from confirming them unintentionally.)
219
218
  5. QuoVadis confirms the user's account and logs them in.
220
219
 
221
- Your new user sign-up form ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/users/new.html.erb)) must include:
220
+ Your new user sign-up form ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/sign_ups/new.html.erb)) must include:
222
221
 
223
222
  - a `:password` field;
224
223
  - optionally a `:password_confirmation` field;
@@ -492,7 +491,7 @@ For example, the default login path is at `/login`. If you set `mount_point` to
492
491
  You must also configure the mailer host so URLs are generated correctly in emails:
493
492
 
494
493
  ```ruby
495
- config.action_mailer.default_url_options: { host: 'example.com }
494
+ config.action_mailer.default_url_options: { host: 'example.com' }
496
495
  ```
497
496
 
498
497
  Finally, you can set up your post-authentication and post-password-change routes. If you don't, you must have a root route. For example:
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QuoVadis
4
- VERSION = '2.0.0'
4
+ VERSION = '2.0.1'
5
5
  end
data/quo_vadis.gemspec CHANGED
@@ -15,10 +15,12 @@ Gem::Specification.new do |spec|
15
15
  # spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
16
16
 
17
17
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
18
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
18
+ `git ls-files -z`.split("\x0")
19
19
  end
20
20
  spec.require_paths = ['lib']
21
21
 
22
- spec.add_dependency 'rails', '>= 6'
23
- spec.add_dependency 'bcrypt', '~> 3.1.7'
22
+ spec.add_dependency 'rails', '>= 6'
23
+ spec.add_dependency 'bcrypt', '~> 3.1.7'
24
+ spec.add_dependency 'rotp', '>= 6'
25
+ spec.add_dependency 'rqrcode', '~> 2.0'
24
26
  end
@@ -0,0 +1 @@
1
+ Only authenticated users can view articles.
@@ -0,0 +1,3 @@
1
+ require_relative 'config/application'
2
+
3
+ Rails.application.load_tasks
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,17 @@
1
+ class ArticlesController < ApplicationController
2
+ before_action :require_authentication, except: :index
3
+ before_action :require_two_factor_authentication, only: :very_secret
4
+
5
+ def index
6
+ end
7
+
8
+ def secret
9
+ end
10
+
11
+ def also_secret
12
+ end
13
+
14
+ def very_secret
15
+ end
16
+
17
+ end
@@ -0,0 +1,42 @@
1
+ # To test sign-ups with confirmation (activation / verification)
2
+ class SignUpsController < ApplicationController
3
+
4
+ around_action :toggle_confirmation
5
+
6
+ def new
7
+ @user = User.new
8
+ end
9
+
10
+
11
+ def create
12
+ @user = User.new user_params
13
+ if @user.save
14
+ if QuoVadis.accounts_require_confirmation
15
+ request_confirmation @user
16
+ redirect_to sign_up_path(@user)
17
+ else
18
+ redirect_to articles_path
19
+ end
20
+ else
21
+ render :new
22
+ end
23
+ end
24
+
25
+ def show
26
+ @user = User.find params[:id]
27
+ end
28
+
29
+ private
30
+
31
+ def user_params
32
+ params.require(:user).permit(:name, :email, :password, :password_confirmation)
33
+ end
34
+
35
+ def toggle_confirmation
36
+ QuoVadis.accounts_require_confirmation true
37
+ yield
38
+ ensure
39
+ QuoVadis.accounts_require_confirmation false
40
+ end
41
+
42
+ end
@@ -0,0 +1,25 @@
1
+ # To test creating users without activation
2
+ class UsersController < ApplicationController
3
+
4
+ def new
5
+ @user = User.new
6
+ end
7
+
8
+ def create
9
+ @user = User.new user_params
10
+ if @user.save
11
+ # NOTE to login the user here, do this:
12
+ # login @user, true
13
+ redirect_to articles_path
14
+ else
15
+ render :new
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def user_params
22
+ params.require(:user).permit(:name, :email, :password, :password_confirmation)
23
+ end
24
+
25
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,3 @@
1
+ class Article < ApplicationRecord
2
+ validates :title, presence: true
3
+ end
@@ -0,0 +1,6 @@
1
+ class Person < ApplicationRecord
2
+ validates :username, presence: true, uniqueness: {case_sensitive: false}
3
+ validates :email, presence: true, uniqueness: {case_sensitive: false}
4
+
5
+ authenticates identifier: :username
6
+ end
@@ -0,0 +1,6 @@
1
+ class User < ApplicationRecord
2
+ validates :name, presence: true
3
+ validates :email, presence: true, uniqueness: {case_sensitive: false}
4
+
5
+ authenticates
6
+ end
@@ -0,0 +1 @@
1
+ <p>Also-secret Articles</p>
@@ -0,0 +1 @@
1
+ <p>Public Articles</p>
@@ -0,0 +1 @@
1
+ <p>Secret Articles</p>
@@ -0,0 +1,2 @@
1
+ <p>Very Secret Articles</p>
2
+
@@ -0,0 +1,46 @@
1
+ <html>
2
+ <head>
3
+ <meta charset="utf-8">
4
+ <%= csrf_meta_tags %>
5
+ <style>
6
+ nav {
7
+ border-bottom: 1px solid #ddd;
8
+ padding: 10px 0;
9
+ }
10
+ nav span {
11
+ margin: 0 5px;
12
+ }
13
+ main {
14
+ margin-top: 20px;
15
+ }
16
+ </style>
17
+ </head>
18
+ <body>
19
+ <nav>
20
+ <% if logged_in? %>
21
+ <span>Logged in as: <%= authenticated_model.email %></span>
22
+ <span><%= link_to 'Sessions', quo_vadis.sessions_path %></span>
23
+ <span><%= link_to 'Change password', quo_vadis.edit_password_path %></span>
24
+ <span><%= link_to '2FA', quo_vadis.twofa_path %></span>
25
+ <span><%= link_to 'Logs', quo_vadis.logs_path %></span>
26
+ <span><%= button_to 'Log out', quo_vadis.logout_path, method: :delete, form: {style: 'display:inline-block'} %></span>
27
+ <% else %>
28
+ <span><%= link_to 'Add user (without confirmation)', main_app.new_user_path %></span>
29
+ <span><%= link_to 'Sign up (with confirmation)', main_app.new_sign_up_path %></span>
30
+ <span><%= link_to 'Log in', quo_vadis.login_path %></span>
31
+ <% end %>
32
+ </nav>
33
+
34
+ <main>
35
+ <section>
36
+ <% %w[notice alert].select { |k| flash.key? k }.each do |k| %>
37
+ <div class="flash flash-<%= k %>">
38
+ <%= flash[k] %>
39
+ </div>
40
+ <% end %>
41
+ </section>
42
+
43
+ <%= yield %>
44
+ </main>
45
+ </body>
46
+ </html>
@@ -0,0 +1,10 @@
1
+ <h1>Confirm account</h1>
2
+
3
+ <%= form_with url: confirmation_path(params[:token]), method: :put do |f| %>
4
+
5
+ <p>
6
+ <label>Please click the button to confirm your account:</label>
7
+ <%= f.submit %>
8
+ </p>
9
+
10
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <h1>Account confirmation</h1>
2
+
3
+ <p>Please check your email.</p>
4
+
5
+ <p><%= link_to 'Request a new email', new_confirmation_path %></p>
@@ -0,0 +1,16 @@
1
+ <h1>Resend confirmation instructions</h1>
2
+
3
+ <%# Note that in this example the User identifier is :email. %>
4
+
5
+ <p>If you didn't receive the confirmation email, please enter your email address and we'll send you another one.</p>
6
+
7
+ <%= form_with url: confirmations_path, method: :post do |f| %>
8
+ <p>
9
+ <%= f.label :email %>
10
+ <%= f.text_field :email, inputmode: 'email', autocomplete: 'email' %>
11
+ </p>
12
+
13
+ <p>
14
+ <%= f.submit %>
15
+ </p>
16
+ <% end %>
@@ -0,0 +1,28 @@
1
+ <h1>Logs</h1>
2
+
3
+ <table>
4
+ <thead>
5
+ <tr>
6
+ <th>Action</th>
7
+ <th>IP</th>
8
+ <th>Metadata</th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <% @logs.each do |log| %>
13
+ <tr>
14
+ <td><%= log.action %></td>
15
+ <td><%= log.ip %></td>
16
+ <td><%= log.metadata.empty? ? '' : log.metadata.map {|k,v| "#{k}: #{v}"}.join(', ') %></td>
17
+ </tr>
18
+ <% end %>
19
+ </tbody>
20
+ </table>
21
+
22
+ <% if @prev_page %>
23
+ <%= link_to 'Newer', logs_path(page: @prev_page) %>
24
+ <% end %>
25
+
26
+ <% if @next_page %>
27
+ <%= link_to 'Older', logs_path(page: @next_page) %>
28
+ <% end %>
@@ -0,0 +1,4 @@
1
+ You can confirm your account here:
2
+
3
+ <%= @url %>
4
+
@@ -0,0 +1,8 @@
1
+ Your email address was changed just now.
2
+
3
+ Location: <%= @ip %>
4
+ Time: <%= @timestamp.strftime '%e %B at %H:%M (%Z)' %>
5
+
6
+ If this was you, you don't need to do anything.
7
+
8
+ If this wasn't you, please let us know.
@@ -0,0 +1,8 @@
1
+ Your <%= @identifier %> was changed just now.
2
+
3
+ Location: <%= @ip %>
4
+ Time: <%= @timestamp.strftime '%e %B at %H:%M (%Z)' %>
5
+
6
+ If this was you, you don't need to do anything.
7
+
8
+ If this wasn't you, please let us know.
@@ -0,0 +1,8 @@
1
+ Your password was changed just now.
2
+
3
+ Location: <%= @ip %>
4
+ Time: <%= @timestamp.strftime '%e %B at %H:%M (%Z)' %>
5
+
6
+ If this was you, you don't need to do anything.
7
+
8
+ If this wasn't you, please let us know.
@@ -0,0 +1,8 @@
1
+ Your password was reset just now.
2
+
3
+ Location: <%= @ip %>
4
+ Time: <%= @timestamp.strftime '%e %B at %H:%M (%Z)' %>
5
+
6
+ If this was you, you don't need to do anything.
7
+
8
+ If this wasn't you, please let us know.
@@ -0,0 +1,8 @@
1
+ Recovery codes for two-factor authentication were generated for your account just now.
2
+
3
+ Location: <%= @ip %>
4
+ Time: <%= @timestamp.strftime '%e %B at %H:%M (%Z)' %>
5
+
6
+ If this was you, you don't need to do anything.
7
+
8
+ If this wasn't you, please let us know.
@@ -0,0 +1,4 @@
1
+ You can reset your password here:
2
+
3
+ <%= @url %>
4
+
@@ -0,0 +1,6 @@
1
+ Your two-factor authentication code was reused just now.
2
+
3
+ It was rejected and whoever reused it was not able to log in.
4
+
5
+ Location: <%= @ip %>
6
+ Time: <%= @timestamp.strftime '%e %B at %H:%M (%Z)' %>
@@ -0,0 +1,8 @@
1
+ Two-factor authentication was set up on your account just now.
2
+
3
+ Location: <%= @ip %>
4
+ Time: <%= @timestamp.strftime '%e %B at %H:%M (%Z)' %>
5
+
6
+ If this was you, you don't need to do anything.
7
+
8
+ If this wasn't you, please let us know.