Skip to content

Commit 0b162d3

Browse files
committed
Joomla! 4.3.2 Stable
1 parent 6e57d75 commit 0b162d3

File tree

17 files changed

+114
-11
lines changed

17 files changed

+114
-11
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE `#__user_mfa` ADD COLUMN `tries` int NOT NULL DEFAULT 0 /** CAN FAIL **/;
2+
ALTER TABLE `#__user_mfa` ADD COLUMN `last_try` datetime /** CAN FAIL **/;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE "#__user_mfa" ADD COLUMN "tries" bigint DEFAULT 0 NOT NULL /** CAN FAIL **/;
2+
ALTER TABLE "#__user_mfa" ADD COLUMN "last_try" timestamp without time zone /** CAN FAIL **/;

administrator/components/com_users/config.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,30 @@
329329
default=""
330330
showon="mfaredirectonlogin:1"
331331
/>
332+
333+
<field
334+
name="mfatrycount"
335+
type="number"
336+
label="COM_USERS_CONFIG_MFATRYCOUNT_LABEL"
337+
filter="integer"
338+
min="0"
339+
max="20"
340+
step="1"
341+
default="10"
342+
validate="number"
343+
/>
344+
345+
<field
346+
name="mfatrytime"
347+
type="number"
348+
label="COM_USERS_CONFIG_MFATRYTIME_LABEL"
349+
filter="integer"
350+
min="1"
351+
max="24"
352+
step="1"
353+
default="1"
354+
validate="number"
355+
/>
332356
</fieldset>
333357

334358
<fieldset

administrator/components/com_users/src/Controller/CaptiveController.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,18 @@ public function validate($cachable = false, $urlparameters = [])
152152
throw new RuntimeException(Text::_('COM_USERS_MFA_INVALID_METHOD'), 500);
153153
}
154154

155+
if (!$model->checkTryLimit($record)) {
156+
// The try limit is reached, show error and return
157+
$captiveURL = Route::_('index.php?option=com_users&view=captive&task=select', false);
158+
$message = Text::_('COM_USERS_MFA_TRY_LIMIT_REACHED');
159+
$this->setRedirect($captiveURL, $message, 'error');
160+
161+
$event = new NotifyActionLog('onComUsersCaptiveValidateTryLimitReached');
162+
$this->app->getDispatcher()->dispatch($event->getName(), $event);
163+
164+
return;
165+
}
166+
155167
// Validate the code
156168
$user = $this->app->getIdentity()
157169
?: Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById(0);
@@ -210,6 +222,8 @@ function (bool $carry, $result) {
210222
$jNow = Date::getInstance();
211223

212224
$record->last_used = $jNow->toSql();
225+
$record->tries = 0;
226+
$record->last_try = null;
213227
$record->store();
214228

215229
// Flag the user as fully logged in

administrator/components/com_users/src/Controller/MethodController.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Joomla\CMS\MVC\Controller\BaseController as BaseControllerAlias;
2020
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
2121
use Joomla\CMS\Router\Route;
22+
use Joomla\CMS\Uri\Uri;
2223
use Joomla\CMS\User\User;
2324
use Joomla\CMS\User\UserFactoryInterface;
2425
use Joomla\Component\Users\Administrator\Helper\Mfa as MfaHelper;
@@ -203,7 +204,7 @@ public function regenerateBackupCodes($cachable = false, $urlparams = []): void
203204
$redirectUrl = 'index.php?option=com_users&task=method.edit&user_id=' . $userId . '&id=' . $backupCodesRecord->id;
204205
$returnURL = $this->input->getBase64('returnurl');
205206

206-
if (!empty($returnURL)) {
207+
if (!empty($returnURL) && Uri::isInternal(base64_decode($returnURL))) {
207208
$redirectUrl .= '&returnurl=' . $returnURL;
208209
}
209210

@@ -258,7 +259,7 @@ public function delete($cachable = false, $urlparams = []): void
258259
$url = Route::_('index.php?option=com_users&task=methods.display&user_id=' . $userId, false);
259260
$returnURL = $this->input->getBase64('returnurl');
260261

261-
if (!empty($returnURL)) {
262+
if (!empty($returnURL) && Uri::isInternal(base64_decode($returnURL))) {
262263
$url = base64_decode($returnURL);
263264
}
264265

@@ -289,7 +290,7 @@ public function save($cachable = false, $urlparams = []): void
289290
$url = Route::_('index.php?option=com_users&task=methods.display&user_id=' . $userId, false);
290291
$returnURL = $this->input->getBase64('returnurl');
291292

292-
if (!empty($returnURL)) {
293+
if (!empty($returnURL) && Uri::isInternal(base64_decode($returnURL))) {
293294
$url = base64_decode($returnURL);
294295
}
295296

administrator/components/com_users/src/Controller/MethodsController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public function disable($cachable = false, $urlparams = []): void
101101
$url = Route::_('index.php?option=com_users&task=methods.display&user_id=' . $userId, false);
102102
$returnURL = $this->input->getBase64('returnurl');
103103

104-
if (!empty($returnURL)) {
104+
if (!empty($returnURL) && Uri::isInternal(base64_decode($returnURL))) {
105105
$url = base64_decode($returnURL);
106106
}
107107

@@ -188,7 +188,7 @@ public function doNotShowThisAgain($cachable = false, $urlparams = []): void
188188
$url = Uri::base();
189189
$returnURL = $this->input->getBase64('returnurl');
190190

191-
if (!empty($returnURL)) {
191+
if (!empty($returnURL) && Uri::isInternal(base64_decode($returnURL))) {
192192
$url = base64_decode($returnURL);
193193
}
194194

administrator/components/com_users/src/Model/CaptiveModel.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Exception;
1414
use Joomla\CMS\Application\CMSApplication;
1515
use Joomla\CMS\Component\ComponentHelper;
16+
use Joomla\CMS\Date\Date;
1617
use Joomla\CMS\Event\MultiFactor\Captive;
1718
use Joomla\CMS\Factory;
1819
use Joomla\CMS\Language\Text;
@@ -409,4 +410,43 @@ private function getAllowedModulePositions(): array
409410

410411
return $res;
411412
}
413+
414+
/**
415+
* Method to check if the mfa method in question has reached it's usage limit
416+
*
417+
* @param MfaTable $method Mfa method record
418+
*
419+
* @return boolean true if user can use the method, false if not
420+
*
421+
* @since 4.3.2
422+
* @throws \Exception
423+
*/
424+
public function checkTryLimit(MfaTable $method)
425+
{
426+
$params = ComponentHelper::getParams('com_users');
427+
$jNow = Date::getInstance();
428+
$maxTries = (int) $params->get('mfatrycount', 10);
429+
$blockHours = (int) $params->get('mfatrytime', 1);
430+
431+
$lastTryTime = strtotime($method->last_try) ?: 0;
432+
$hoursSinceLastTry = (strtotime(Factory::getDate()->toSql()) - $lastTryTime) / 3600;
433+
434+
if ($method->last_try !== null && $hoursSinceLastTry > $blockHours) {
435+
// If it's been long enough, start a new reset count
436+
$method->last_try = null;
437+
$method->tries = 0;
438+
} elseif ($method->tries < $maxTries) {
439+
// If we are under the max count, just increment the counter
440+
++$method->tries;
441+
$method->last_try = $jNow->toSql();
442+
} else {
443+
// At this point, we know we have exceeded the maximum resets for the time period
444+
return false;
445+
}
446+
447+
// Store changes to try counter and/or the timestamp
448+
$method->store();
449+
450+
return true;
451+
}
412452
}

administrator/components/com_users/src/Table/MfaTable.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
* @property array $options Configuration options for the MFA Method.
4444
* @property string $created_on Date and time the record was created.
4545
* @property string $last_used Date and time the record was last used successfully.
46+
* @property int $tries Counter for unsuccessful tries
47+
* @property string $last_try Date and time of the last unsuccessful try
4648
*
4749
* @since 4.2.0
4850
*/

administrator/components/com_users/src/View/Method/HtmlView.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Joomla\CMS\Toolbar\Button\LinkButton;
1818
use Joomla\CMS\Toolbar\Toolbar;
1919
use Joomla\CMS\Toolbar\ToolbarHelper;
20+
use Joomla\CMS\Uri\Uri;
2021
use Joomla\CMS\User\User;
2122
use Joomla\Component\Users\Administrator\Model\MethodModel;
2223

@@ -168,7 +169,9 @@ function ($x) {
168169
}
169170

170171
$returnUrl = empty($this->returnURL) ? '' : base64_decode($this->returnURL);
171-
$returnUrl = $returnUrl ?: Route::_('index.php?option=com_users&task=methods.display&user_id=' . $this->user->id);
172+
$returnUrl = ($returnUrl && Uri::isInternal($returnUrl))
173+
? $returnUrl
174+
: Route::_('index.php?option=com_users&task=methods.display&user_id=' . $this->user->id);
172175

173176
if ($this->isAdmin && $this->getLayout() === 'edit') {
174177
$button = (new BasicButton('user-mfa-edit-save'))

administrator/language/en-GB/com_users.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ COM_USERS_CONFIG_SAVE_FAILED="An error was encountered while saving the configur
8181
COM_USERS_CONFIG_SILENTRESPONSES_DESC="For experts. A comma–separated list of Joomla authentication response types which are considered silent logins. The default is <code>cookie</code> (the Remember Me feature) and <code>passwordless</code> (WebAuthn)."
8282
COM_USERS_CONFIG_SILENTRESPONSES_LABEL="Silent login authentication response types (for experts)"
8383
COM_USERS_CONFIG_USER_OPTIONS="User Options"
84+
COM_USERS_CONFIG_MFATRYCOUNT_LABEL="Maximum MFA tries"
85+
COM_USERS_CONFIG_MFATRYTIME_LABEL="MFA limit block time (in hours)"
8486
COM_USERS_COUNT_DISABLED_USERS="Blocked Users"
8587
COM_USERS_COUNT_ENABLED_USERS="Enabled Users"
8688
COM_USERS_DASHBOARD_TITLE="Users Dashboard"
@@ -271,6 +273,7 @@ COM_USERS_MFA_FIRSTTIME_NOTINTERESTED="Don't show this again"
271273
COM_USERS_MFA_FIRSTTIME_PAGE_HEAD="Set up your Multi-factor Authentication"
272274
COM_USERS_MFA_INVALID_CODE="Multi-factor Authentication failed. Please try again."
273275
COM_USERS_MFA_INVALID_METHOD="Invalid Multi-factor Authentication method."
276+
COM_USERS_MFA_TRY_LIMIT_REACHED="You have reached the try limit for the currently selected Authentication method. Please choose a different method or wait."
274277
COM_USERS_MFA_LBL_CREATEDON="Added: %s"
275278
COM_USERS_MFA_LBL_DATE_FORMAT_PAST="F d, Y"
276279
COM_USERS_MFA_LBL_DATE_FORMAT_TODAY="H:i"

0 commit comments

Comments
 (0)