Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 4 additions & 138 deletions wcfsetup/install/files/acp/templates/userAuthenticationFailureList.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<header class="contentHeader">
<div class="contentHeaderTitle">
<h1 class="contentTitle">{lang}wcf.acp.user.authentication.failure.list{/lang}{if $items} <span class="badge badgeInverse">{#$items}</span>{/if}</h1>
<h1 class="contentTitle">{lang}wcf.acp.user.authentication.failure.list{/lang} <span class="badge badgeInverse">{#$gridView->countRows()}</span></h1>
</div>

{hascontent}
Expand All @@ -14,142 +14,8 @@
{/hascontent}
</header>

<form method="post" action="{link controller='UserAuthenticationFailureList'}{/link}">
<section class="section">
<h2 class="sectionTitle">{lang}wcf.global.filter{/lang}</h2>

<div class="row rowColGap formGrid">
<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<select name="filter[environment]" id="environment">
<option value="">{lang}wcf.acp.user.authentication.failure.environment{/lang}</option>
<option value="admin"{if $filter[environment] === 'admin'} selected{/if}>{lang}wcf.acp.user.authentication.failure.environment.admin{/lang}</option>
<option value="user"{if $filter[environment] === 'user'} selected{/if}>{lang}wcf.acp.user.authentication.failure.environment.user{/lang}</option>
</select>
</dd>
</dl>

<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<input type="text" id="username" name="filter[username]" value="{$filter[username]}" placeholder="{lang}wcf.user.username{/lang}" class="long">
</dd>
</dl>

<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<select name="filter[validationError]" id="validationError">
<option value="">{lang}wcf.acp.user.authentication.failure.validationError{/lang}</option>
<option value="invalidPassword"{if $filter[validationError] === 'invalidPassword'} selected{/if}>{lang}wcf.acp.user.authentication.failure.validationError.invalidPassword{/lang}</option>
<option value="invalidUsername"{if $filter[validationError] === 'invalidUsername'} selected{/if}>{lang}wcf.acp.user.authentication.failure.validationError.invalidUsername{/lang}</option>
{event name='validationErrorFilterOptions'}
</select>
</dd>
</dl>

<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<input type="date" id="startDate" name="filter[startDate]" value="{$filter[startDate]}" data-placeholder="{lang}wcf.acp.user.authentication.failure.time.start{/lang}">
</dd>
</dl>

<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<input type="date" id="endDate" name="filter[endDate]" value="{$filter[endDate]}" data-placeholder="{lang}wcf.acp.user.authentication.failure.time.end{/lang}">
</dd>
</dl>

<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<input type="text" id="userAgent" name="filter[userAgent]" value="{$filter[userAgent]}" placeholder="{lang}wcf.user.userAgent{/lang}" class="long">
</dd>
</dl>

{event name='filterFields'}
</div>

<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
{csrfToken}
</div>
</section>
</form>

{hascontent}
<div class="paginationTop">
{content}
{pages print=true assign=pagesLinks controller='UserAuthenticationFailureList' link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder$filterLinkParameters"}
{/content}
</div>
{/hascontent}

{if $objects|count}
<div class="section tabularBox">
<table class="table">
<thead>
<tr>
<th class="columnID columnFailureID{if $sortField == 'failureID'} active {@$sortOrder}{/if}"><a href="{link controller='UserAuthenticationFailureList'}pageNo={@$pageNo}&sortField=failureID&sortOrder={if $sortField == 'failureID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
<th class="columnText columnEnvironment{if $sortField == 'environment'} active {@$sortOrder}{/if}"><a href="{link controller='UserAuthenticationFailureList'}pageNo={@$pageNo}&sortField=environment&sortOrder={if $sortField == 'environment' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.user.authentication.failure.environment{/lang}</a></th>
<th class="columnTitle columnUsername{if $sortField == 'username'} active {@$sortOrder}{/if}"><a href="{link controller='UserAuthenticationFailureList'}pageNo={@$pageNo}&sortField=username&sortOrder={if $sortField == 'username' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.user.username{/lang}</a></th>
<th class="columnDate columnTime{if $sortField == 'time'} active {@$sortOrder}{/if}"><a href="{link controller='UserAuthenticationFailureList'}pageNo={@$pageNo}&sortField=time&sortOrder={if $sortField == 'time' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.user.authentication.failure.time{/lang}</a></th>
<th class="columnText columnValidationError{if $sortField === 'validationError'} active {@$sortOrder}{/if}"><a href="{link controller='UserAuthenticationFailureList'}pageNo={@$pageNo}&sortField=validationError&sortOrder={if $sortField === 'validationError' && $sortOrder === 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.user.authentication.failure.validationError{/lang}</a></th>
<th class="columnURL columnIpAddress{if $sortField == 'ipAddress'} active {@$sortOrder}{/if}"><a href="{link controller='UserAuthenticationFailureList'}pageNo={@$pageNo}&sortField=ipAddress&sortOrder={if $sortField == 'ipAddress' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.user.ipAddress{/lang}</a></th>
<th class="columnText columnUserAgent{if $sortField == 'userAgent'} active {@$sortOrder}{/if}"><a href="{link controller='UserAuthenticationFailureList'}pageNo={@$pageNo}&sortField=userAgent&sortOrder={if $sortField == 'userAgent' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.user.userAgent{/lang}</a></th>

{event name='columnHeads'}
</tr>
</thead>

<tbody>
{foreach from=$objects item='authenticationFailure'}
<tr>
<td class="columnID columnFailureID">{@$authenticationFailure->failureID}</td>
<td class="columnText columnEnvironment">{lang}wcf.acp.user.authentication.failure.environment.{@$authenticationFailure->environment}{/lang}</td>
<td class="columnTitle columnUsername">
{if $authenticationFailure->userID}
<a href="{link controller='UserEdit' id=$authenticationFailure->userID}{/link}">{$authenticationFailure->username}</a>
{else}
{$authenticationFailure->username}
{/if}
</td>
<td class="columnDate columnTime">{@$authenticationFailure->time|time}</td>
<td class="columnSmallText columnValidationError">
{if $authenticationFailure->validationError}
{lang}wcf.acp.user.authentication.failure.validationError.{$authenticationFailure->validationError}{/lang}
{/if}
</td>
<td class="columnSmallText columnIpAddress">{$authenticationFailure->getIpAddress()}</td>
<td class="columnSmallText columnUserAgent" title="{$authenticationFailure->userAgent}">{$authenticationFailure->userAgent|truncate:75|tableWordwrap}</td>

{event name='columns'}
</tr>
{/foreach}
</tbody>
</table>
</div>

<footer class="contentFooter">
{hascontent}
<div class="paginationBottom">
{content}{@$pagesLinks}{/content}
</div>
{/hascontent}

{hascontent}
<nav class="contentFooterNavigation">
<ul>
{content}{event name='contentFooterNavigation'}{/content}
</ul>
</nav>
{/hascontent}
</footer>
{else}
<woltlab-core-notice type="info">{lang}wcf.global.noItems{/lang}</woltlab-core-notice>
{/if}
<div class="section">
{unsafe:$gridView->render()}
</div>

{include file='footer'}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

namespace wcf\acp\page;

use wcf\data\user\authentication\failure\UserAuthenticationFailureList;
use wcf\page\SortablePage;
use wcf\system\WCF;
use wcf\page\AbstractGridViewPage;
use wcf\system\gridView\AbstractGridView;
use wcf\system\gridView\admin\UserAuthenticationFailureGridView;

/**
* Shows a list of user authentication failures.
*
* @author Marcel Werk
* @copyright 2001-2019 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @author Olaf Braun, Marcel Werk
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*
* @property UserAuthenticationFailureList $objectList
* @property UserAuthenticationFailureGridView $gridView
*/
class UserAuthenticationFailureListPage extends SortablePage
class UserAuthenticationFailureListPage extends AbstractGridViewPage
{
/**
* @inheritDoc
Expand All @@ -32,118 +32,9 @@ class UserAuthenticationFailureListPage extends SortablePage
*/
public $neededModules = ['ENABLE_USER_AUTHENTICATION_FAILURE'];

/**
* @inheritDoc
*/
public $defaultSortField = 'time';

/**
* @inheritDoc
*/
public $defaultSortOrder = 'DESC';

/**
* @inheritDoc
*/
public $validSortFields = ['failureID', 'environment', 'userID', 'username', 'time', 'ipAddress', 'userAgent', 'validationError'];

/**
* @inheritDoc
*/
public $objectListClassName = UserAuthenticationFailureList::class;

/**
* @var string[]
* @since 5.4
*/
public $filter = [
'environment' => '',
'endDate' => '',
'startDate' => '',
'username' => '',
'userAgent' => '',
'validationError' => '',
];

/**
* @inheritDoc
*/
public function readParameters()
{
parent::readParameters();

if (isset($_REQUEST['filter']) && \is_array($_REQUEST['filter'])) {
foreach ($_REQUEST['filter'] as $key => $value) {
if (\array_key_exists($key, $this->filter)) {
$this->filter[$key] = $value;
}
}
}
}

/**
* @inheritDoc
*/
protected function initObjectList()
#[\Override]
protected function createGridViewController(): AbstractGridView
{
parent::initObjectList();

if ($this->filter['environment'] !== '') {
$this->objectList->getConditionBuilder()->add(
'user_authentication_failure.environment = ?',
[$this->filter['environment']]
);
}
if ($this->filter['endDate'] !== '') {
$endDate = @\strtotime($this->filter['endDate']);
if ($endDate > 0) {
$this->objectList->getConditionBuilder()->add(
'user_authentication_failure.time <= ?',
[$endDate]
);
}
}
if ($this->filter['startDate'] !== '') {
$startDate = @\strtotime($this->filter['startDate']);
if ($startDate > 0) {
$this->objectList->getConditionBuilder()->add(
'user_authentication_failure.time >= ?',
[$startDate]
);
}
}
if ($this->filter['username'] !== '') {
$this->objectList->getConditionBuilder()->add(
'user_authentication_failure.username LIKE ?',
['%' . \addcslashes($this->filter['username'], '_%') . '%']
);
}
if ($this->filter['userAgent'] !== '') {
$this->objectList->getConditionBuilder()->add(
'user_authentication_failure.userAgent LIKE ?',
['%' . \addcslashes($this->filter['userAgent'], '_%') . '%']
);
}
if ($this->filter['validationError'] !== '') {
$this->objectList->getConditionBuilder()->add(
'user_authentication_failure.validationError = ?',
[$this->filter['validationError']]
);
}
}

/**
* @inheritDoc
*/
public function assignVariables()
{
parent::assignVariables();

$filterLinkParameters = \http_build_query(['filter' => \array_filter($this->filter)], '', '&');

WCF::getTPL()->assign([
'filter' => $this->filter,
'filterLinkParameters' => $filterLinkParameters ? '&' . $filterLinkParameters : '',
]);
return new UserAuthenticationFailureGridView();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* @property-read int $time timestamp at which the user authentication failure has occurred
* @property-read string $ipAddress ip address of the user trying to login in
* @property-read string $userAgent user agent of the user trying to login in
* @property-read string $validationError
*/
class UserAuthenticationFailure extends DatabaseObject
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace wcf\event\gridView\admin;

use wcf\event\IPsr14Event;
use wcf\system\gridView\admin\UserAuthenticationFailureGridView;

/**
* Indicates that the user authentication failure grid view has been initialized.
*
* @author Olaf Braun
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/
final class UserAuthenticationFailureGridViewInitialized implements IPsr14Event
{
public function __construct(public readonly UserAuthenticationFailureGridView $param)
{
}
}
Loading