Add logging of authentication successes and failures

master
Lars Vierbergen 7 years ago
parent 7d75c50869
commit 85e202506a
  1. 104
      Entity/AuthenticationEntry.php
  2. 119
      EventListener/AuthenticationStatsListener.php
  3. 1
      EventListener/LoginStatsListener.php
  4. 5
      Resources/config/services.xml
  5. 47
      Resources/migrations/VersionAuthserverStats20171102211518.php

@ -0,0 +1,104 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) 2017 Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace vierbergenlars\AuthserverStatsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="vierbergenlars_stats_auth")
*/
class AuthenticationEntry
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*
* @var integer
*/
private $id;
/**
* @ORM\Column(name="ip", type="string")
*
* @var string
*/
private $ip;
/**
* @ORM\Column(name="ts", type="datetime")
*
* @var \DateTime
*/
private $timeStamp;
/**
* @ORM\Column(name="success", type="boolean")
*
* @var boolean
*/
private $success;
public function __construct($ip, $success)
{
$this->ip = $ip;
$this->success = $success;
$this->timeStamp = new \DateTime();
}
/**
*
* @return number
*/
public function getId()
{
return $this->id;
}
/**
*
* @return string
*/
public function getIp()
{
return $this->ip;
}
/**
*
* @return \DateTime
*/
public function getTimeStamp()
{
return $this->timeStamp;
}
/**
*
* @return boolean
*/
public function isSuccess()
{
return $this->success;
}
}

@ -0,0 +1,119 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) 2017 Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace vierbergenlars\AuthserverStatsBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use vierbergenlars\AuthserverStatsBundle\Event\StatsEvent;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
use vierbergenlars\AuthserverStatsBundle\Entity\AuthenticationEntry;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
class AuthenticationStatsListener implements EventSubscriberInterface
{
/**
*
* @var RegistryInterface
*/
private $registry;
/**
*
* @var RequestStack
*/
private $requestStack;
public static function getSubscribedEvents()
{
return [
StatsEvent::class => [
[
'getAuthStats',
-1
]
],
AuthenticationEvents::AUTHENTICATION_SUCCESS => [
'onAuthSuccess'
],
AuthenticationEvents::AUTHENTICATION_FAILURE => [
'onAuthFailure'
]
];
}
public function __construct(RegistryInterface $registry, RequestStack $requestStack)
{
$this->registry = $registry;
$this->requestStack = $requestStack;
}
public function onAuthSuccess(AuthenticationEvent $event)
{
if ($event->getAuthenticationToken() instanceof AnonymousToken)
return;
$authSuccess = new AuthenticationEntry($this->requestStack->getMasterRequest()->getClientIp(), true);
$em = $this->registry->getManagerForClass(AuthenticationEntry::class);
$em->persist($authSuccess);
$em->flush($authSuccess);
}
public function onAuthFailure(AuthenticationFailureEvent $event)
{
$authFailure = new AuthenticationEntry($this->requestStack->getMasterRequest()->getClientIp(), false);
$em = $this->registry->getManagerForClass(AuthenticationEntry::class);
$em->persist($authFailure);
$em->flush($authFailure);
}
public function getAuthStats(StatsEvent $event)
{
if (!$event->isEnabled('login'))
return;
$event->setMuninConfig('login', $event->getMuninConfig('login') + [
'auth_succ.label' => 'Authentication passes',
'auth_succ.type' => 'COUNTER',
'auth_fail.label' => 'Authentication failures',
'auth_fail.type' => 'COUNTER'
]);
$queryBuilder = $this->registry->getRepository(AuthenticationEntry::class)->createQueryBuilder('e');
/* @var $queryBuilder \Doctrine\ORM\QueryBuilder */
$queryBuilder->select('count(e)', 'e.success')->groupBy('e.success');
$rawStats = $queryBuilder->getQuery()->getArrayResult();
$stats = [
'login.auth_succ' => 0,
'login.auth_fail' => 0
];
foreach ($rawStats as $rawStat) {
if ($rawStat['success']) {
$stats['login.auth_succ'] += $rawStat['1'];
} else {
$stats['login.auth_fail'] += $rawStat['1'];
}
}
$event->addStatistics($stats);
}
}

@ -25,6 +25,7 @@ use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use vierbergenlars\AuthserverStatsBundle\Entity\LoginEntry;
use Symfony\Component\Security\Core\AuthenticationEvents;
class LoginStatsListener implements EventSubscriberInterface
{

@ -31,5 +31,10 @@
<argument type="service" id="doctrine" />
<tag name="kernel.event_subscriber" />
</service>
<service class="vierbergenlars\AuthserverStatsBundle\EventListener\AuthenticationStatsListener">
<argument type="service" id="doctrine" />
<argument type="service" id="request_stack" />
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>

@ -0,0 +1,47 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
class VersionAuthserverStats20171102211518 extends AbstractMigration
{
public function up(Schema $schema)
{
$authEntry = $schema->createTable('vierbergenlars_stats_auth');
$authEntry->addColumn('id', 'integer')->setAutoincrement(true);
$authEntry->addColumn('ip', 'string');
$authEntry->addColumn('ts', 'datetime');
$authEntry->addColumn('success', 'boolean');
$authEntry->setPrimaryKey([
'id'
]);
}
public function down(Schema $schema)
{
$schema->dropTable('vierbergenlars_stats_auth');
}
}