trusty-cms 7.0.28 → 7.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +15 -1
  4. data/INSTALL.md +21 -3
  5. data/README.md +0 -1
  6. data/app/assets/stylesheets/admin/modules/_buttons.scss +4 -1
  7. data/app/assets/stylesheets/admin/partials/_forms.scss +4 -0
  8. data/app/controllers/admin/security_controller.rb +78 -0
  9. data/app/controllers/admin/sessions_controller.rb +43 -0
  10. data/app/controllers/admin/two_factor_controller.rb +42 -0
  11. data/app/controllers/admin/users_controller.rb +9 -0
  12. data/app/controllers/application_controller.rb +7 -0
  13. data/app/models/standard_tags.rb +0 -16
  14. data/app/models/trusty_cms/config.rb +0 -4
  15. data/app/models/user.rb +1 -1
  16. data/app/views/admin/configuration/edit.html.haml +0 -7
  17. data/app/views/admin/configuration/show.html.haml +21 -12
  18. data/app/views/admin/preferences/edit.html.haml +1 -4
  19. data/app/views/admin/security/edit.html.haml +57 -0
  20. data/app/views/{devise → admin}/sessions/new.html.haml +5 -5
  21. data/app/views/admin/two_factor/show.html.haml +14 -0
  22. data/app/views/admin/users/_form.html.haml +0 -3
  23. data/app/views/admin/users/index.html.haml +22 -1
  24. data/app/views/devise/passwords/edit.html.haml +2 -4
  25. data/config/initializers/active_record_encryption.rb +5 -0
  26. data/config/initializers/devise.rb +4 -0
  27. data/config/initializers/trusty_cms_config.rb +0 -1
  28. data/config/locales/en.yml +30 -5
  29. data/config/routes.rb +9 -6
  30. data/db/migrate/20250502162215_add_devise_two_factor_to_admins.rb +7 -0
  31. data/lib/generators/trusty_cms/templates/application.rb.erb +1 -1
  32. data/lib/trusty_cms/admin_ui.rb +12 -7
  33. data/lib/trusty_cms/engine.rb +0 -3
  34. data/lib/trusty_cms/version.rb +1 -1
  35. data/package.json +1 -1
  36. data/spec/dummy/config/application.rb +1 -1
  37. data/spec/dummy/config/initializers/trusty_cms_config.rb +0 -1
  38. data/trusty_cms.gemspec +2 -0
  39. data/yarn.lock +4 -4
  40. metadata +38 -18
  41. data/app/assets/javascripts/rad_social/jquery.validate.min.js +0 -4
  42. data/app/assets/javascripts/rad_social/rad_ajax_form.js +0 -82
  43. data/app/assets/javascripts/rad_social/rad_email.js +0 -5
  44. data/app/assets/javascripts/rad_social/rad_email_form.js +0 -80
  45. data/app/assets/javascripts/rad_social/rad_email_validator.js +0 -47
  46. data/app/assets/javascripts/rad_social/rad_widget.js +0 -20
  47. data/app/assets/stylesheets/rad_social/rad_screen.scss +0 -141
  48. data/app/controllers/social_mailer_controller.rb +0 -26
  49. data/app/helpers/rad_social_helper.rb +0 -20
  50. data/app/mailers/rad_social_mailer.rb +0 -25
  51. data/app/views/admin/users/_password_fields.html.haml +0 -18
  52. data/app/views/rad_social_mailer/social_mail.html.haml +0 -5
  53. data/app/views/rad_social_mailer/social_mail_form.html.haml +0 -39
  54. data/app/views/widget/_email_form.html.haml +0 -20
  55. data/app/views/widget/_horizontal_widget.html.haml +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16753e005b09f59c333c5e9fd2af4bca3bd0e20f8d2da054500b3d262d1eecfa
4
- data.tar.gz: c4159171a6a9f932b685cd3855bd0f931392053d4d797025e73aae1a5aa443fc
3
+ metadata.gz: 9ef6650284d438676ef388a1b002d3ff0acfe81dc068bcfafa9e1cb2814749f9
4
+ data.tar.gz: e6a980c1afa8435a04314a5b43b6788ecc4f743e9c7c32015245e15e0c8c0804
5
5
  SHA512:
6
- metadata.gz: 3f91892e8c3e8b0aa0e3899b7796833a4e2a0e5074eacf92f5e47b7e216559b5c4d32f487f03fb725775d960d20f0cd15cea678a0a14ef42aa65c9d07cb531c2
7
- data.tar.gz: f5735dc96aa6cf0c3c80686f36d48cd7d49f5b8865ffd4c2d02ab708170764b5bff91a7fb525acbff932e67c76b9adcd104f35e145ee71ce61c4c897e42c597f
6
+ metadata.gz: 33d9139d690d376c8d4547b3d8c20cec750671050a9aa9378a197c3c28ab79cb04fda4403b17f2da86bdad23c5699d3dfdeef5f16db1469ad17c546464df89b8
7
+ data.tar.gz: f728eb8f4da2af5477ba994c9ccad2d5ac46620f0893295734a66035601c2317dc750985ec3b9d52518c786eebd1f5056a4f8dd301cb2beb4001f82e7d96835c
data/Gemfile CHANGED
@@ -15,6 +15,7 @@ group :development, :test do
15
15
  gem 'activestorage-validator'
16
16
  gem 'acts_as_list'
17
17
  gem 'database_cleaner'
18
+ gem 'devise-two-factor'
18
19
  gem 'factory_bot_rails', '6.4.4'
19
20
  gem 'file_validators'
20
21
  gem 'launchy', '~> 3.0.1'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- trusty-cms (7.0.28)
4
+ trusty-cms (7.0.30)
5
5
  RedCloth (= 4.3.3)
6
6
  activestorage-validator
7
7
  acts_as_list (>= 0.9.5, < 1.3.0)
@@ -11,6 +11,7 @@ PATH
11
11
  ckeditor (>= 4.2.2, < 4.4.0)
12
12
  delocalize (>= 0.2, < 2.0)
13
13
  devise
14
+ devise-two-factor
14
15
  drb
15
16
  execjs (~> 2.7)
16
17
  haml (>= 5.0, < 6.0)
@@ -32,6 +33,7 @@ PATH
32
33
  ransack (~> 4.2.1)
33
34
  rdoc (>= 5.1, < 7.0)
34
35
  roadie-rails
36
+ rqrcode
35
37
  sass-rails
36
38
  stringex (>= 2.7.1, < 2.9.0)
37
39
  tzinfo (>= 1.2.3, < 2.1.0)
@@ -133,6 +135,7 @@ GEM
133
135
  xpath (~> 3.2)
134
136
  childprocess (5.1.0)
135
137
  logger (~> 1.5)
138
+ chunky_png (1.4.0)
136
139
  ckeditor (4.3.0)
137
140
  orm_adapter (~> 0.5.0)
138
141
  terrapin
@@ -159,6 +162,11 @@ GEM
159
162
  railties (>= 4.1.0)
160
163
  responders
161
164
  warden (~> 1.2.3)
165
+ devise-two-factor (6.1.0)
166
+ activesupport (>= 7.0, < 8.1)
167
+ devise (~> 4.0)
168
+ railties (>= 7.0, < 8.1)
169
+ rotp (~> 6.0)
162
170
  diff-lcs (1.5.1)
163
171
  docile (1.4.1)
164
172
  drb (2.2.1)
@@ -346,6 +354,11 @@ GEM
346
354
  roadie-rails (3.3.0)
347
355
  railties (>= 5.1, < 8.1)
348
356
  roadie (~> 5.0)
357
+ rotp (6.3.0)
358
+ rqrcode (3.1.0)
359
+ chunky_png (~> 1.0)
360
+ rqrcode_core (~> 2.0)
361
+ rqrcode_core (2.0.0)
349
362
  rspec-core (3.13.2)
350
363
  rspec-support (~> 3.13.0)
351
364
  rspec-expectations (3.13.3)
@@ -430,6 +443,7 @@ DEPENDENCIES
430
443
  activestorage-validator
431
444
  acts_as_list
432
445
  database_cleaner
446
+ devise-two-factor
433
447
  factory_bot_rails (= 6.4.4)
434
448
  file_validators
435
449
  launchy (~> 3.0.1)
data/INSTALL.md CHANGED
@@ -6,8 +6,9 @@ From within the directory containing your TrustyCMS instance:
6
6
 
7
7
  2. Add the following gems to your Gemfile:
8
8
 
9
- - gem 'trusty-cms'
10
- - gem 'rails-observers'
9
+ - gem 'trusty-cms'
10
+ - gem 'rails-observers'
11
+ - gem 'devise-two-factor'
11
12
 
12
13
  3. Run `bundle install`
13
14
 
@@ -20,5 +21,22 @@ From within the directory containing your TrustyCMS instance:
20
21
 
21
22
  7. Add utf8 encoding to your db.yml
22
23
 
23
- 8. Run `bundle exec rake db:setup`, `bundle exec rake trusty_cms:install:migrations`, then
24
+ 8. Set up encryption keys required for Rails’ native encryption (used by features like two-factor authentication):
25
+
26
+ - Run the encryption initializer command:
27
+
28
+ ```bash
29
+ ./bin/rails db:encryption:init
30
+ ```
31
+
32
+ - This will output three secrets. Copy the values and set them as environment variables in your preferred environment file (e.g., `.env`, `.env.development`, or via system environment settings):
33
+
34
+ ```env
35
+ ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
36
+ ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
37
+ ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
38
+ ```
39
+ - **Important**: Use different keys for each environment (development, test, production) unless two environments (e.g., development and staging) share a database — in that case, they **must** use the same keys.
40
+
41
+ 9. Run `bundle exec rake db:setup`, `bundle exec rake trusty_cms:install:migrations`, then
24
42
  `bundle exec rake db:bootstrap`.
data/README.md CHANGED
@@ -26,7 +26,6 @@ TrustyCMS features:
26
26
  * An advanced plugin system
27
27
  * Asset management & searching
28
28
  * Serve multiple sites (domains) from a single instance
29
- * Social sharing buttons
30
29
  * Reusable bits of content (Snippets)
31
30
  * Allows Rails controllers/actions to use Trusty CMS layouts as their "layout"
32
31
  * Operates in two modes: dev and production depending on the URL
@@ -18,7 +18,6 @@
18
18
  text-decoration: none;
19
19
  }
20
20
 
21
-
22
21
  .cancel-button {
23
22
  @include button;
24
23
  background-color: $light-red;
@@ -30,3 +29,7 @@
30
29
  color: $white;
31
30
  }
32
31
  }
32
+
33
+ .button-margin-top {
34
+ margin-top: 0.5em;
35
+ }
@@ -111,3 +111,7 @@ textarea {
111
111
  #search-input {
112
112
  max-width: 25em;
113
113
  }
114
+
115
+ .form-margin-top {
116
+ margin-top: 0.5em;
117
+ }
@@ -0,0 +1,78 @@
1
+ require 'rqrcode'
2
+
3
+ class Admin::SecurityController < ApplicationController
4
+ before_action :authenticate_user!
5
+ before_action :set_user
6
+ before_action :set_template_names
7
+ before_action :ensure_otp_secret, only: %i[show edit]
8
+ before_action :set_two_factor_variables, only: %i[show edit]
9
+
10
+ def show
11
+ set_standard_body_style
12
+ render :edit
13
+ end
14
+
15
+ def edit
16
+ render
17
+ end
18
+
19
+ def update
20
+ if @user.update(security_params)
21
+ sign_out(@user)
22
+ redirect_to new_user_session_path, notice: t('security_controller.password_updated')
23
+ else
24
+ flash[:error] = t('security_controller.error_updating_password')
25
+ render :edit
26
+ end
27
+ end
28
+
29
+ def verify_two_factor
30
+ if @user.validate_and_consume_otp!(params[:otp_attempt])
31
+ @user.update!(otp_required_for_login: true)
32
+ redirect_to admin_security_path, notice: t('security_controller.two_factor_enabled')
33
+ else
34
+ flash[:error] = t('security_controller.two_factor_invalid_code')
35
+ redirect_to admin_security_path
36
+ end
37
+ end
38
+
39
+ def disable_two_factor
40
+ if @user.update(otp_required_for_login: false, otp_secret: nil)
41
+ redirect_to admin_security_path, notice: t('security_controller.two_factor_disabled')
42
+ else
43
+ flash[:error] = t('security_controller.two_factor_disabled_error')
44
+ redirect_to admin_security_path
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def set_user
51
+ @user = current_user
52
+ end
53
+
54
+ def set_template_names
55
+ @controller_name = 'user'
56
+ @template_name = 'security'
57
+ end
58
+
59
+ def ensure_otp_secret
60
+ return if @user.otp_secret.present?
61
+
62
+ @user.update!(otp_secret: User.generate_otp_secret)
63
+ end
64
+
65
+ def set_two_factor_variables
66
+ @two_factor_enabled = @user.otp_required_for_login
67
+
68
+ return if @two_factor_enabled
69
+
70
+ otp_uri = @user.otp_provisioning_uri(@user.email, issuer: 'TrustyCMS')
71
+ qr = RQRCode::QRCode.new(otp_uri)
72
+ @qr_png_data = qr.as_png(size: 200).to_data_url
73
+ end
74
+
75
+ def security_params
76
+ params.require(:user).permit(:password, :password_confirmation)
77
+ end
78
+ end
@@ -0,0 +1,43 @@
1
+ class Admin::SessionsController < Devise::SessionsController
2
+ def create
3
+ user = find_user
4
+
5
+ if authenticated?(user)
6
+ handle_successful_authentication(user)
7
+ else
8
+ handle_failed_authentication
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def find_user
15
+ User.find_by(email: params[:user][:email])
16
+ end
17
+
18
+ def authenticated?(user)
19
+ user&.valid_password?(params[:user][:password])
20
+ end
21
+
22
+ def handle_successful_authentication(user)
23
+ if user.otp_required_for_login
24
+ start_two_factor_session(user)
25
+ redirect_to admin_two_factor_path
26
+ else
27
+ sign_in(:user, user)
28
+ redirect_to after_sign_in_path_for(user)
29
+ end
30
+ end
31
+
32
+ def handle_failed_authentication
33
+ self.resource = resource_class.new(sign_in_params)
34
+ clean_up_passwords(resource)
35
+ flash.now[:alert] = t('invalid_email_or_password')
36
+ render :new
37
+ end
38
+
39
+ def start_two_factor_session(user)
40
+ session[:pre_2fa_user_id] = user.id
41
+ session[:pre_2fa_started_at] = Time.current.to_i
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ class Admin::TwoFactorController < ApplicationController
2
+ skip_before_action :authenticate_user!
3
+ before_action :load_pre_2fa_user
4
+
5
+ MAX_2FA_SESSION_DURATION = 5.minutes.freeze
6
+
7
+ def show; end
8
+
9
+ def create
10
+ if @user.validate_and_consume_otp!(params[:otp_attempt])
11
+ session.delete(:pre_2fa_user_id)
12
+ session.delete(:pre_2fa_started_at)
13
+ sign_in(:user, @user)
14
+ redirect_to after_sign_in_path_for(@user)
15
+ else
16
+ reset_session
17
+ redirect_to new_user_session_path, alert: t('two_factor_controller.invalid_code')
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def load_pre_2fa_user
24
+ if current_user
25
+ redirect_to after_sign_in_path_for(current_user) and return
26
+ end
27
+
28
+ @user = User.find_by(id: session[:pre_2fa_user_id])
29
+
30
+ if !@user&.otp_required_for_login || session_expired?
31
+ reset_session
32
+ redirect_to new_user_session_path, alert: t('two_factor_controller.session_expired')
33
+ end
34
+ end
35
+
36
+ def session_expired?
37
+ started_at = session[:pre_2fa_started_at]
38
+ return true unless started_at
39
+
40
+ Time.current.to_i - started_at > MAX_2FA_SESSION_DURATION
41
+ end
42
+ end
@@ -45,6 +45,15 @@ class Admin::UsersController < Admin::ResourceController
45
45
  end
46
46
  end
47
47
 
48
+ def disable_2fa
49
+ user = User.find(params[:id])
50
+ user.update(
51
+ otp_required_for_login: false,
52
+ otp_secret: nil,
53
+ )
54
+ redirect_to admin_users_path
55
+ end
56
+
48
57
  private
49
58
 
50
59
  def user_params
@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
6
6
 
7
7
  protect_from_forgery with: :exception
8
8
  before_action :authenticate_user!
9
+ before_action :configure_permitted_parameters, if: :devise_controller?
9
10
  before_action :set_timezone
10
11
  before_action :set_user_locale
11
12
  before_action :set_javascripts_and_stylesheets
@@ -44,6 +45,12 @@ class ApplicationController < ActionController::Base
44
45
  end
45
46
  end
46
47
 
48
+ protected
49
+
50
+ def configure_permitted_parameters
51
+ devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt])
52
+ end
53
+
47
54
  private
48
55
 
49
56
  def set_mailer
@@ -931,22 +931,6 @@ module StandardTags
931
931
  end
932
932
  end
933
933
 
934
- desc 'Widget of sharing icons'
935
- tag 'rad_share_widget' do |tag|
936
- attributes = tag.attr.to_options
937
- url = attributes[:url].nil? ? request.url : attributes[:url]
938
- message = attributes[:message].nil? ? "Check out #{tag.locals.page.title}." : attributes[:message]
939
- email_subject = attributes[:email_subject].nil? ? tag.locals.page.title : attributes[:email_subject]
940
- email_message = attributes[:email_message].nil? ? "I thought you might be interested in this: #{url}" : "#{attributes[:email_message]} #{url}"
941
- email_action_url = attributes[:email_action_url].nil? ? '/rad_social/mail' : attributes[:email_action_url]
942
- request.env['action_controller.instance'].render_to_string partial: 'widget/horizontal_widget',
943
- locals: { url: url,
944
- message: message,
945
- email_subject: email_subject,
946
- email_message: email_message,
947
- email_action_url: email_action_url }
948
- end
949
-
950
934
  private
951
935
 
952
936
  def render_children_with_pagination(tag, opts = {})
@@ -145,10 +145,6 @@ module TrustyCms
145
145
  @default_settings ||= %w{defaults.locale defaults.page.filter defaults.page.parts defaults.page.fields defaults.page.status}
146
146
  end
147
147
 
148
- def user_settings
149
- @user_settings ||= ['user.allow_password_reset?']
150
- end
151
-
152
148
  # A convenient drying method for specifying a prefix and options common to several settings.
153
149
  #
154
150
  # TrustyCms.config do |config|
data/app/models/user.rb CHANGED
@@ -3,7 +3,7 @@ class User < ActiveRecord::Base
3
3
  self.table_name = 'admins'
4
4
 
5
5
  # :confirmable, :lockable, :timeoutable and :omniauthable
6
- devise :database_authenticatable, :registerable,
6
+ devise :two_factor_authenticatable, :registerable,
7
7
  :recoverable, :rememberable, :trackable, :validatable
8
8
 
9
9
  alias_attribute :created_by_id, :id
@@ -23,13 +23,6 @@
23
23
  %p
24
24
  = edit_config default_setting
25
25
 
26
- - form.edit_users do
27
- %fieldset
28
- %h4 Passwords
29
- - TrustyCms.config.user_settings.each do |user_setting|
30
- %p
31
- = edit_config user_setting
32
-
33
26
  - render_region :form_bottom do |form_bottom|
34
27
  - form_bottom.edit_buttons do
35
28
  .buttons
@@ -1,4 +1,4 @@
1
- #preferences.box
1
+ %fieldset
2
2
  - render_region :user do |user|
3
3
  - user.preferences do
4
4
  %h3
@@ -13,10 +13,6 @@
13
13
  = t('email_address')
14
14
  %span.uri
15
15
  = current_user.email
16
- %p.ruled
17
- %label
18
- = t('password')
19
- %big &bull;&bull;&bull;&bull;&bull;
20
16
  %p.ruled
21
17
  %label
22
18
  = t('language')
@@ -25,7 +21,25 @@
25
21
  .actions
26
22
  = button_to t("edit_preferences"), edit_admin_user_path(current_user), :method => :get
27
23
 
28
- #config.box
24
+ %fieldset
25
+ - render_region :user do |user|
26
+ - user.preferences do
27
+ %h3
28
+ = t('security')
29
+ %p.ruled
30
+ %label
31
+ = t('password')
32
+ %span.value
33
+ &bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;
34
+ %p.ruled
35
+ %label
36
+ = t('two_factor_authentication')
37
+ %span
38
+ = current_user.otp_required_for_login ? t('enabled') : t('disabled')
39
+ .actions
40
+ = button_to t('edit_security_settings'), admin_security_path, :method => :get
41
+
42
+ %fieldset
29
43
  - render_region :trusty_config do |config|
30
44
  - config.site do
31
45
  %h3
@@ -40,11 +54,6 @@
40
54
  %p.ruled
41
55
  = show_config default_setting
42
56
 
43
- - config.users do
44
- %h4 Passwords
45
- - TrustyCms.config.user_settings.each do |user_setting|
46
- %p.ruled
47
- = show_config user_setting
48
57
  - if current_user.admin?
49
58
  .actions
50
- = button_to t("edit_configuration"), edit_admin_configuration_path, :method => :get
59
+ = button_to t("edit_configuration"), edit_admin_configuration_path, :method => :get
@@ -27,12 +27,9 @@
27
27
  = f.label :email, t("email_address"), :class => 'optional'
28
28
  = f.text_field 'email', :class => 'textbox', :maxlength => 255
29
29
 
30
- - form.edit_password do
31
- = render 'admin/users/password_fields', :f => f
32
-
33
30
  - render_region :form_bottom, :locals => {:f => f} do |form_bottom|
34
31
  - form_bottom.edit_buttons do
35
32
  .buttons
36
33
  = save_model_button @user
37
34
  = t('or')
38
- = link_to t('cancel'), admin_pages_url, class: 'alt'
35
+ = link_to t('cancel'), admin_configuration_path, class: 'alt'
@@ -0,0 +1,57 @@
1
+ - @page_title = @user.name + ' - Security'
2
+ - body_classes << 'edit_security'
3
+
4
+ - render_region :main do |main|
5
+ - main.edit_header do
6
+ %h1
7
+ = t('security')
8
+
9
+ - main.edit_form do
10
+ %fieldset
11
+ %h3
12
+ = t('change_password')
13
+ = form_for @user, :url => admin_security_path, :html => { :method => :put, 'data-onsubmit_status' => "#{t('saving_changes')}&#8230;" } do |f|
14
+ - render_region :form, :locals => {:f => f} do |form|
15
+ - form.edit_password do
16
+ %fieldset#change-password
17
+ %p
18
+ = f.label :password, t('new_password')
19
+ = f.password_field 'password', :value => '', :maxlength => 40, :autocomplete => 'new-password'
20
+ %p
21
+ = f.label :password_confirmation, t('password_confirmation')
22
+ = f.password_field 'password_confirmation', :value => '', :maxlength => 40, :autocomplete => 'new-password'
23
+
24
+ - render_region :form_bottom, :locals => {:f => f} do |form_bottom|
25
+ - form_bottom.edit_buttons do
26
+ .buttons
27
+ = save_model_button @user
28
+
29
+ - main.two_factor do
30
+ %fieldset
31
+ %h3
32
+ = t('two_factor_authentication')
33
+
34
+ - if @two_factor_enabled
35
+ %fieldset
36
+ %p
37
+ = t('security_controller.two_factor_is_enabled')
38
+ = button_to t('disable'),
39
+ disable_two_factor_admin_security_path,
40
+ method: :post,
41
+ data: { confirm: t('security_controller.two_factor_disable_confirm') },
42
+ class: "button button-margin-top"
43
+ - else
44
+ %fieldset
45
+ %p
46
+ = t('security_controller.scan_qr_instructions')
47
+ = image_tag @qr_png_data, alt: t('security_controller.qr_alt')
48
+ %p
49
+ = t('security_controller.manual_key_instructions')
50
+ %strong= @user.otp_secret
51
+
52
+ .form-margin-top
53
+ = form_with url: verify_two_factor_admin_security_path, method: :post do |form|
54
+ %div
55
+ = form.label :otp_attempt, t('security_controller.enter_qr_code')
56
+ = form.text_field :otp_attempt, autofocus: true, size: 6, maxlength: 6
57
+ = form.submit t('security_controller.verify_and_enable')
@@ -1,10 +1,10 @@
1
1
  - body_classes << 'login_form'
2
2
  .login-form-content
3
3
  .visual
4
- = image_tag('/assets/admin/default_safe_login.svg', alt: 'web browser with padlock on top')
4
+ = image_tag('/assets/admin/default_safe_login.svg', alt: 'Web browser with padlock on top')
5
5
  .login
6
- %h1 Log in
7
- = form_for(resource, as: resource_name, url: authenticate_path) do |f|
6
+ %h1 Log In
7
+ = form_for(resource, as: resource_name, url: user_session_path) do |f|
8
8
  .field
9
9
  = f.label :email
10
10
  = f.email_field :email, autofocus: true, autocomplete: 'email'
@@ -18,8 +18,8 @@
18
18
  Remember Me
19
19
  %br
20
20
  .actions
21
- = f.submit 'Log in'
21
+ = f.submit 'Log In'
22
22
  - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
23
23
  = link_to 'Forgot your password?', new_password_path(resource_name)
24
24
  - if flash.alert
25
- .error= flash.alert
25
+ .error= flash.alert
@@ -0,0 +1,14 @@
1
+ - body_classes << 'login_form'
2
+ .login-form-content
3
+ .visual
4
+ = image_tag('/assets/admin/default_safe_login.svg', alt: 'Web browser with padlock on top')
5
+ .login
6
+ %h1
7
+ = t('two_factor_authentication')
8
+ = form_with url: admin_two_factor_path, method: :post, local: true do |form|
9
+ .field
10
+ = form.label :otp_attempt, t('security_controller.enter_qr_code')
11
+ = form.text_field :otp_attempt, autofocus: true, maxlength: 6, size: 6
12
+ %br
13
+ .actions
14
+ = form.submit t('security_controller.verify_code')
@@ -15,9 +15,6 @@
15
15
  = f.label :email, t('email_address') , :class => 'optional'
16
16
  = f.text_field 'email', :class => 'textbox', :maxlength => 255
17
17
 
18
- - form.edit_password do
19
- = render 'password_fields', :f => f
20
-
21
18
  - form.edit_roles do
22
19
  - if current_user.admin?
23
20
  %fieldset.multi_option
@@ -11,8 +11,14 @@
11
11
  %th.user== #{t('name')} / #{t('username')}
12
12
  - thead.roles_header do
13
13
  %th.roles= t('roles')
14
+ - thead.two_factor_status_header do
15
+ %th.two_factor_status
16
+ = t('two_factor_authentication_status')
17
+ - thead.disable_two_factor_header do
18
+ %th.disable_two_factor
19
+ Disable 2FA
14
20
  - thead.actions_header do
15
- %th.actions{:style=>'width:9em'}= t('modify')
21
+ %th.actions= t('modify')
16
22
  - thead.last_sign_in_at_header do
17
23
  %th.last_sign_in_at= "Last Sign In"
18
24
  %tbody
@@ -24,6 +30,21 @@
24
30
  = link_to user.name, edit_admin_user_url(user)
25
31
  - tbody.roles_cell do
26
32
  %td.roles= roles(user)
33
+ - tbody.two_factor_status_cell do
34
+ %td.two_factor
35
+ - if user.otp_required_for_login
36
+ = t('enabled')
37
+ - else
38
+ = t('disabled')
39
+ - tbody.disable_two_factor_cell do
40
+ %td.actions
41
+ - if user.otp_required_for_login
42
+ - if !current_user.admin?
43
+ %span.action.disabled
44
+ %i.fas.fa-minus-circle
45
+ Disable
46
+ - else
47
+ = button_to 'Disable', disable_2fa_admin_user_path(user), method: :patch, data: { confirm: "Are you sure you want to disable 2FA for this user?" }
27
48
  - tbody.actions_cell do
28
49
  %td.actions
29
50
  - if !current_user.admin?
@@ -8,16 +8,14 @@
8
8
  = f.hidden_field :reset_password_token
9
9
  .field
10
10
  = f.label :password, 'New password'
11
- %br/
12
11
  - if @minimum_password_length
13
12
  %em
14
13
  (#{@minimum_password_length} characters minimum)
15
- %br/
14
+
16
15
  = f.password_field :password, autofocus: true, autocomplete: 'new-password'
16
+ %br/
17
17
  .field
18
18
  = f.label :password_confirmation, 'Confirm new password'
19
- %br/
20
19
  = f.password_field :password_confirmation, autocomplete: 'new-password'
21
20
  .actions
22
21
  = f.submit 'Change my password'
23
- = render 'devise/shared/links'
@@ -0,0 +1,5 @@
1
+ ActiveRecord::Encryption.configure(
2
+ primary_key: ENV["ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY"],
3
+ deterministic_key: ENV["ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY"],
4
+ key_derivation_salt: ENV["ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT"]
5
+ )