[TikiWiki-commits] [Git][tikiwiki/tiki][master] [NEW] 2FA: Allow site admins to reset or disable 2FA per user

"Merci Jacob \(@mercihabam\) via TikiWiki-cvs" <[email protected]>
Newsgroups gmane.comp.cms.tiki.cvs
Message-ID <68f8eb7be6c88_2c7511381602a@gitlab-sidekiq-low-urgency-cpu-bound-v2-65cbf47f7b-2rjjr.mail>

Merci Jacob pushed to branch master at Tiki Wiki CMS Groupware / Tiki


Commits:
a7ba8b44 by Merci Jacob at 2025-10-22T14:23:11+00:00
[NEW] 2FA: Allow site admins to reset or disable 2FA per user
---
* add js only when the pref is enabled

* fix linter

* reset user 2FA on "reset button" click, and show proper feedback

* [NEW] 2FA: Allow site admins to reset or disable 2FA per user

See merge request tikiwiki/tiki!8723

- - - - -


6 changed files:

- lib/core/Services/User/Controller.php
- path_js_importmap_generator.php
- + src/js/jquery-tiki/tiki-admin_2fa.js
- templates/tiki-adminusers.tpl
- themes/base_files/scss/_element-plus-tiki.scss
- tiki-adminusers.php


Changes:

=====================================
lib/core/Services/User/Controller.php
=====================================
@@ -1340,4 +1340,45 @@ class Services_User_Controller
         TikiLib::lib('tiki')->set_user_preference($user, 'trk_items_column_pref', $columns);
         return tr('Your column preference for tracker "%0" is saved.', $tracker_name);
     }
+
+    public function actionReset2FA($input)
+    {
+        Services_Exception_Denied::checkGlobal('admin_users');
+
+        $user = $input->user->text();
+        if (empty($user)) {
+            throw new Services_Exception(tra('No user was specified'));
+        }
+
+        TikiLib::lib('user')->update_2_factor_secret($user, '');
+        return ['success' => true, 'message' => tra('2FA secret reset successfully')];
+    }
+
+    public function actionToggle2FA($input)
+    {
+        Services_Exception_Denied::checkGlobal('admin_users');
+
+        $user = $input->user->text();
+        if (empty($user)) {
+            throw new Services_Exception(tra('No user was specified'));
+        }
+
+        $tikilib = TikiLib::lib('tiki');
+
+        $usersDisabled2FA = $tikilib->get_preference('twoFactorAuthExcludedUsers', expectArray: true);
+        $usersEnabled2FA = $tikilib->get_preference('twoFactorAuthIncludedUsers', expectArray: true);
+
+        if ($input->enable->bool()) {
+            $usersEnabled2FA = array_unique(array_merge($usersEnabled2FA, [$user]));
+            $usersDisabled2FA = array_diff($usersDisabled2FA, [$user]);
+        } else {
+            $usersDisabled2FA = array_unique(array_merge($usersDisabled2FA, [$user]));
+            $usersEnabled2FA = array_diff($usersEnabled2FA, [$user]);
+        }
+
+        $tikilib->set_preference('twoFactorAuthExcludedUsers', $usersDisabled2FA);
+        $tikilib->set_preference('twoFactorAuthIncludedUsers', $usersEnabled2FA);
+
+        return ['success' => true];
+    }
 }


=====================================
path_js_importmap_generator.php
=====================================
@@ -67,6 +67,7 @@ function generateJsImportmapScripts(bool $useBaseUrl = false)
                 "@jquery-tiki/tiki-svgedit_draw" => $tikiUrl . JS_ASSETS_PATH . "/jquery-tiki/tiki-svgedit_draw.js",
                 "@jquery-tiki/tiki-handle_svgedit" => $tikiUrl . JS_ASSETS_PATH . "/jquery-tiki/tiki-handle_svgedit.js",
                 "@jquery-tiki/tiki-admin_menu_options" => $tikiUrl . JS_ASSETS_PATH . "/jquery-tiki/tiki-admin_menu_options.js",
+                "@jquery-tiki/tiki-admin_2fa" => $tikiUrl . JS_ASSETS_PATH . "/jquery-tiki/tiki-admin_2fa.js",
                 "@jquery-tiki/tiki-edit_structure" => $tikiUrl . JS_ASSETS_PATH . "/jquery-tiki/tiki-edit_structure.js",
                 "@jquery-tiki/wikiplugin-trackercalendar" => $tikiUrl . JS_ASSETS_PATH . "/jquery-tiki/wikiplugin-trackercalendar.js",
                 "@jquery-tiki/fullcalendar_to_pdf" => $tikiUrl . JS_ASSETS_PATH . "/jquery-tiki/fullcalendar_to_pdf.js",


=====================================
src/js/jquery-tiki/tiki-admin_2fa.js
=====================================
@@ -0,0 +1,41 @@
+$("#force2FA").on("change", function () {
+    $.tikiModal(tr("Saving..."));
+    $.post($.service("user", "toggle2FA"), { enable: $(this).is(":checked"), user: $(this).data("user") })
+        .done(function () {
+            showMessage(tr("Settings saved"), "success");
+        })
+        .fail(function () {
+            showMessage(tr("Failed to save settings"), "error");
+        })
+        .always(function () {
+            $.tikiModal();
+        });
+});
+
+$("#reset2FA").on("click", function () {
+    $.openModal({
+        title: tr("Reset 2FA"),
+        content: tr("Are you sure you want to reset Two Factor Authentication method for this user? They will need to set it up again."),
+        buttons: [
+            {
+                text: tr("Reset"),
+                type: "danger",
+                onClick: function () {
+                    $.closeModal();
+                    $.tikiModal(tr("Resetting..."));
+                    $.post($.service("user", "reset2FA"), { user: $("#reset2FA").data("user") })
+                        .done(function () {
+                            showMessage(tr("2FA has been reset"), "success");
+                            $("#reset2FA").prop("disabled", true);
+                        })
+                        .fail(function () {
+                            showMessage(tr("Failed to reset 2FA"), "error");
+                        })
+                        .always(function () {
+                            $.tikiModal();
+                        });
+                },
+            },
+        ],
+    });
+});


=====================================
templates/tiki-adminusers.tpl
=====================================
@@ -598,6 +598,23 @@
                         </div>
                     {/if}
 
+                    {if $prefs.twoFactorAuth eq 'y'}
+                        <div class="mb-3 row">
+                            <label class="col-sm-3 col-md-2 col-form-label" for="force2FA">{tr}2FA{/tr}</label>
+                            <div class="form-check form-switch col-sm-7 col-md-6">
+                                <input class="form-check-input" type="checkbox" role="switch" id="force2FA" data-user="{$userinfo.login}" {if $force2FA}checked{/if}>
+                                <small class="form-text text-muted">{tr}Enable/Disable Two Factor Authentication for this user.{/tr}</small>
+                            </div>
+                        </div>
+                        <div class="mb-3 row">
+                            <div class="col-sm-3 col-md-2"></div>
+                            <div class="col-sm-7 col-md-6">
+                                <button type="button" class="btn btn-secondary w-100" id="reset2FA" {if not $userinfo.twoFactorSecret}disabled{/if} data-user="{$userinfo.login}">{tr}Reset 2FA{/tr}</button>
+                                <small class="form-text text-muted">{tr}This will require the user to reset up 2FA the next time they log in.{/tr}</small>
+                            </div>
+                        </div>
+                    {/if}
+
                     <div class="mb-3 row">
                         <div class="col-sm-9 offset-sm-3 col-md-10 offset-md-2">
                             {if isset($userinfo.userId) && $userinfo.userId}


=====================================
themes/base_files/scss/_element-plus-tiki.scss
=====================================
@@ -18,4 +18,8 @@ el-select, el-autocomplete, el-input {
 
 el-autocomplete ~ .search_mod_magnifier {
   margin-top: unset !important;
-}
\ No newline at end of file
+}
+
+.el-message {
+  z-index: 9999 !important;
+}


=====================================
tiki-adminusers.php
=====================================
@@ -582,6 +582,10 @@ if (isset($_REQUEST['user']) and $_REQUEST['user']) {
         }
     }
 
+    if ($prefs['twoFactorAuth'] == 'y') {
+        $smarty->assign('force2FA', $userlib->forceTwoFactorAuth($userinfo['login']));
+    }
+
     if ($prefs['email_due'] > 0) {
         $userinfo['daysSinceEmailConfirm'] = floor(($userlib->now - $userinfo['email_confirm']) / (60 * 60 * 24));
     }
@@ -668,6 +672,12 @@ if (count($errors) > 0) {
     Feedback::error(['mes' => $errors]);
 }
 
+$headerlib = TikiLib::lib('header');
+
+if ($prefs['twoFactorAuth'] == 'y') {
+    $headerlib->add_js_module("import '@jquery-tiki/tiki-admin_2fa';");
+}
+
 $smarty->assign_by_ref('all_groups', $all_groups);
 $smarty->assign('userinfo', $userinfo);
 $smarty->assign('userId', $_REQUEST['user']);



View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/a7ba8b442d65a4f0a1f7e76c5cd44766064c3e91

-- 
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/a7ba8b442d65a4f0a1f7e76c5cd44766064c3e91
You're receiving this email because of your account on gitlab.com.

_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.