]> source.dussan.org Git - redmine.git/commitdiff
When 2FA is set to optional, allow to require 2FA only for certain user groups (...
authorMarius Balteanu <marius.balteanu@zitec.com>
Sun, 4 Jul 2021 13:06:47 +0000 (13:06 +0000)
committerMarius Balteanu <marius.balteanu@zitec.com>
Sun, 4 Jul 2021 13:06:47 +0000 (13:06 +0000)
Patch by Jens Krämer.

git-svn-id: http://svn.redmine.org/redmine/trunk@21060 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/models/group.rb
app/models/setting.rb
app/models/user.rb
app/views/groups/_form.html.erb
app/views/settings/_authentication.html.erb
config/locales/en.yml
db/migrate/20210704125704_add_twofa_required_to_groups.rb [new file with mode: 0644]
test/integration/twofa_test.rb

index a84f8650f560e2788a73a6cdcdf1482711488693..a1fd356021d4c914f758034b83f62ecf5750a174 100644 (file)
@@ -41,6 +41,7 @@ class Group < Principal
 
   safe_attributes(
     'name',
+    'twofa_required',
     'user_ids',
     'custom_field_values',
     'custom_fields',
index e5c5476655ae6a9fbeaa058f75d23da15fb06528..dfa054028088ee6090bcdee1fb69ea9af284ca31 100644 (file)
@@ -236,6 +236,14 @@ class Setting < ActiveRecord::Base
     params
   end
 
+  def self.twofa_required?
+    twofa == '2'
+  end
+
+  def self.twofa_optional?
+    twofa == '1'
+  end
+
   # Helper that returns an array based on per_page_options setting
   def self.per_page_options_array
     per_page_options.split(%r{[\s,]}).collect(&:to_i).select {|n| n > 0}.sort
index cc1841a61b7db0f4c8f6c5e2af21d083fc57e00d..2591f56be99c76cb0f7d85b7f519dc00cecc3623 100644 (file)
@@ -407,7 +407,10 @@ class User < Principal
   end
 
   def must_activate_twofa?
-    Setting.twofa == '2' && !twofa_active?
+    (
+      Setting.twofa_required? ||
+      (Setting.twofa_optional? && groups.any?(&:twofa_required?))
+    ) && !twofa_active?
   end
 
   def pref
index 9d5b087e1d1c2f0cd6e735235297242b1b85832c..723b9f2037505b218c85b43f1181897563a7277d 100644 (file)
@@ -3,6 +3,15 @@
 <div class="box tabular">
   <p><%= f.text_field :name, :required => true, :size => 60,
            :disabled => !@group.safe_attribute?('name')  %></p>
+  <% unless @group.builtin? %>
+    <p><%= f.check_box :twofa_required, disabled: !Setting.twofa_optional? %>
+      <% if Setting.twofa_required? %>
+        <em class="info"><%= l 'twofa_text_group_required' %></em>
+      <% elsif !Setting.twofa_optional? %>
+        <em class="info"><%= l 'twofa_text_group_disabled' %></em>
+      <% end %>
+    </p>
+  <% end %>
 
   <% @group.custom_field_values.each do |value| %>
     <p><%= custom_field_tag_with_label :group, value %></p>
index 5522ff5cff1e10c57d6a968d5db75730840c4f72..9fd0ef6463b69dfe01a16a54d515f415b3dacffa 100644 (file)
@@ -34,6 +34,7 @@
                               [l(:label_required_lower), "2"]] -%>
   <em class="info">
     <%= t 'twofa_hint_disabled_html', label: t(:label_disabled) -%><br/>
+    <%= t 'twofa_hint_optional_html', label: t(:label_optional) -%><br/>
     <%= t 'twofa_hint_required_html', label: t(:label_required_lower) -%>
   </em>
 </p>
index 0aeafe516bb991edbd1cedd6b1b87e7251353e0a..488a9b4eeedf50e7c05e2b372da0bb50ca3ed9f9 100644 (file)
@@ -408,6 +408,7 @@ en:
   field_history_default_tab: Issue's history default tab
   field_unique_id: Unique ID
   field_toolbar_language_options: Code highlighting toolbar languages
+  field_twofa_required: Require two factor authentication
 
   setting_app_title: Application title
   setting_welcome_text: Welcome text
@@ -1335,6 +1336,7 @@ en:
   twofa_not_active: "Not activated"
   twofa_label_code: Code
   twofa_hint_disabled_html: Setting <strong>%{label}</strong> will deactivate and unpair two-factor authentication devices for all users.
+  twofa_hint_optional_html: Setting <strong>%{label}</strong> will let users set up two-factor authentication at will, unless it is required by one of their groups.
   twofa_hint_required_html: Setting <strong>%{label}</strong> will require all users to set up two-factor authentication at their next login.
   twofa_label_setup: Enable two-factor authentication
   twofa_label_deactivation_confirmation: Disable two-factor authentication
@@ -1359,6 +1361,7 @@ en:
   twofa_text_backup_codes_hint: Use these codes instead of a one-time password should you not have access to your second factor. Each code can only be used once. It is recommended to print and store them in a safe place.
   twofa_text_backup_codes_created_at: Backup codes generated %{datetime}.
   twofa_backup_codes_already_shown: Backup codes cannot be shown again, please <a data-method="post" href="%{bc_path}">generate new backup codes</a> if required.
-
+  twofa_text_group_required: "This setting is only effective when the global two factor authentication setting is set to 'optional'. Currently, two factor authentication is required for all users."
+  twofa_text_group_disabled: "This setting is only effective when the global two factor authentication setting is set to 'optional'. Currently, two factor authentication is disabled."
   text_user_destroy_confirmation: "Are you sure you want to delete this user and remove all references to them? This cannot be undone. Often, locking a user instead of deleting them is the better solution. To confirm, please enter their login (%{login}) below."
   text_project_destroy_enter_identifier: "To confirm, please enter the project's identifier (%{identifier}) below."
diff --git a/db/migrate/20210704125704_add_twofa_required_to_groups.rb b/db/migrate/20210704125704_add_twofa_required_to_groups.rb
new file mode 100644 (file)
index 0000000..91361bc
--- /dev/null
@@ -0,0 +1,5 @@
+class AddTwofaRequiredToGroups < ActiveRecord::Migration[6.1]
+  def change
+    add_column :users, :twofa_required, :boolean, default: false
+  end
+end
index a787e2770a6ff51d243888ab69dd02046d45f71a..a25fa28957b5ec320ed5851d6799061d7f9704f8 100644 (file)
@@ -24,6 +24,32 @@ class TwofaTest < Redmine::IntegrationTest
 
   test "should require twofa setup when configured" do
     with_settings twofa: "2" do
+      assert Setting.twofa_required?
+      log_user('jsmith', 'jsmith')
+      follow_redirect!
+      assert_redirected_to "/my/twofa/totp/activate/confirm"
+    end
+  end
+
+  test "should require twofa setup when required by group" do
+    user = User.find_by_login 'jsmith'
+    assert_not user.must_activate_twofa?
+
+    group = Group.all.first
+    group.update_column :twofa_required, true
+    group.users << user
+    user.reload
+
+    with_settings twofa: "0" do
+      assert_not Setting.twofa_optional?
+      assert_not Setting.twofa_required?
+      assert_not user.must_activate_twofa?
+    end
+
+    with_settings twofa: "1" do
+      assert Setting.twofa_optional?
+      assert_not Setting.twofa_required?
+      assert user.must_activate_twofa?
       log_user('jsmith', 'jsmith')
       follow_redirect!
       assert_redirected_to "/my/twofa/totp/activate/confirm"