From b2bb71c050ba38c40863f92642abc1fa17ed1d25 Mon Sep 17 00:00:00 2001 From: henk Date: Wed, 23 Dec 2015 17:40:16 +0100 Subject: [PATCH] Issue #15: Remove hard-coded Dutch labels - Controller\GuardStatsController replaced label values by translation keys replaced options and their lookup by methods and calls ::addStatsPeriodForm now passes datetype and datetime pattern to formtype ::translateRelativeDate adapted ::initDateFormatAndPattern, ::initDateTimeTransformer now use configuration parameter and support locale-dependent date formatting - Form\Type\StatsPeriodType now expects and uses datetype and datetime pattern - Resources/translations/metaclass_auth_guard.nl.yml added with Dutch translations - Resources/translations/metaclass_auth_guard.en.yml added with English translations - Resources/views/Guard/statistics_content.html.twig added trans calls - DependencyInjection\Configuration::getConfigTreeBuilder added ui.dateTimeFormat - DependencyInjection\MetaclassAuthenticationGuardExtension::load now sets param: metaclass_auth_guard.ui.dateTimeFormat - Readme.md restored the Web based user interface feature - Resources/doc/Installation.md added doc on: - Bundle parameter metaclass_auth_guard.ui.dateTimeFormat - services parameter metaclass_auth_guard.statistics.StatsPeriod.formType - Available translations (&clone me on Github) Issue #17: Default configuration set without adding to config.yml - DependencyInjection\Configuration::getConfigTreeBuilder added ->addDefaultsIfNotSet() to each arraynode Make web based user interface more extendable: - Resources/config/services.yml added parameters metaclass_auth_guard.statistics.StatsPeriod.formType - Controller\GuardStatsController::addStatsPeriodForm now uses parameter metaclass_auth_guard.statistics.StatsPeriod.formType ::initDateFormatAndPattern may be overridden for setting locale dependent (custom) patterns --- Controller/GuardStatsController.php | 204 +++++++++++------- DependencyInjection/Configuration.php | 9 + .../MetaclassAuthenticationGuardExtension.php | 1 + Form/Type/StatsPeriodType.php | 11 +- README.md | 12 +- Resources/config/services.yml | 3 +- Resources/doc/Installation.md | 28 ++- Resources/doc/changelog.txt | 38 +++- .../translations/metaclass_auth_guard.en.yml | 55 +++++ .../translations/metaclass_auth_guard.nl.yml | 55 +++++ .../views/Guard/statistics_content.html.twig | 14 +- 11 files changed, 329 insertions(+), 101 deletions(-) create mode 100644 Resources/translations/metaclass_auth_guard.en.yml create mode 100644 Resources/translations/metaclass_auth_guard.nl.yml diff --git a/Controller/GuardStatsController.php b/Controller/GuardStatsController.php index 1fe522b..de6fd38 100644 --- a/Controller/GuardStatsController.php +++ b/Controller/GuardStatsController.php @@ -5,42 +5,40 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; +use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Metaclass\AuthenticationGuardBundle\Service\UsernamePasswordFormAuthenticationGuard; use Metaclass\AuthenticationGuardBundle\Form\Type\StatsPeriodType; +use Symfony\Component\PropertyAccess\Exception\RuntimeException; class GuardStatsController extends Controller { - protected $dtTransformer; + /** @var int \IntlDateFormatter datetype derived by ::initDateFormatAndPattern, + * Defaults to DateTimeType::DEFAULT_DATE_FORMAT */ + protected $dateFormat; + /** @var string|null \IntlDateFormatter pattern derived by ::initDateFormatAndPattern */ + protected $dateTimePattern; - // #TODO: use translation, maybe use configuration of controller as a service? - protected $dateTimeFormat = "dd-MM-yyyy HH:mm:ss"; - protected $booleanLabels = array('Nee', 'Ja'); - protected $blockedLabels = array('OK', 'Geblokkeerd'); - protected $rejectionLabels = array( - 'UsernameBlocked' => 'Gebruikersnaam', - 'IpAddressBlocked' => 'Adres', - 'UsernameBlockedForIpAddress' => 'Gebruikersnaam op adres', - ); - - public function __construct() - { - $this->dtTransformer = new DateTimeToLocalizedStringTransformer( - null, null, null, null, \IntlDateFormatter::GREGORIAN, $this->dateTimeFormat); - } + // Cacheing + + /** @var \Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer */ + protected $dtTransformer; + /** @var array with translated relative date words like 'minutes', 'hours', 'days' */ + protected $translateRelativeDateArray; /** * @Route("/statistics", name="Guard_statistics") */ public function statisticsAction() { + $this->initDateTimeTransformer(); $governor = $this->get('metaclass_auth_guard.tresholds_governor'); + $params['title'] = $this->get('translator')->trans('statistics.title', array(), 'metaclass_auth_guard'); $params['routes']['this'] = 'Guard_statistics'; - $params['labels'] = array('show' => 'Bekijken'); $params['action_params'] = array(); $this->addStatisticCommonParams($params, $governor); @@ -53,11 +51,12 @@ public function statisticsAction() $limitFrom = $this->getRequest()->isMethod('POST') ? null : $countingSince; - $limits = $this->addStatsPeriodForm($params, $governor, 'IP Adressen', $limitFrom); + $limits = $this->addStatsPeriodForm($params, $governor, 'StatsPeriod.statistics', $limitFrom); if (isSet($limits['From'])) { $this->addCountsGroupedTableParams($params, $governor, $limits); $params['blockedHeaderIndent'] = 6; + $params['labels'] = array('show' => 'history.show'); $params['route_history'] = 'Guard_history'; $params['limits']['From'] = $this->dtTransformer->transform($limits['From']); $params['limits']['Until'] = $this->dtTransformer->transform($limits['Until']); @@ -73,16 +72,17 @@ public function statisticsAction() */ public function historyAction($ipAddress) { + $this->initDateTimeTransformer(); $governor = $this->get('metaclass_auth_guard.tresholds_governor'); $params['routes']['this'] = 'Guard_history'; $params['action_params'] = array('ipAddress' => $ipAddress); - $params['title'] = 'Inloghistorie'; + $params['title'] = $this->get('translator')->trans('history.title', array(), 'metaclass_auth_guard'); $this->buildMenu($params, 'Guard_history'); $params['fieldSpec'] = array( 'IP Adres' => 'ipAddress', - 'Maximum per gebruikersnaam' => 'limitPerUserName', - 'Maximum per Adres' => 'limitBasePerIpAddress', + 'tresholds_governor_params.limitPerUserName' => 'limitPerUserName', + 'tresholds_governor_params.limitBasePerIpAddress' => 'limitBasePerIpAddress', ); $params['fieldValues'] = array( 'ipAddress' => $ipAddress, @@ -90,13 +90,13 @@ public function historyAction($ipAddress) 'limitBasePerIpAddress' => $governor->limitBasePerIpAddress, ); - $limits = $this->addStatsPeriodForm($params, $governor, 'Historie'); + $limits = $this->addStatsPeriodForm($params, $governor, 'StatsPeriod.history'); if (isSet($limits['From'])) { $history = $governor->requestCountsManager->countsByAddressBetween($ipAddress, $limits['From'], $limits['Until']); - $this->addHistoryTableParams($params, $history, 'username', 'Naam'); + $this->addHistoryTableParams($params, $history, 'username', 'secu_requests.col.username'); $params['route_byUsername'] = 'Guard_statisticsByUserName'; - $params['labels'] = array('show' => 'Bekijken'); + $params['labels'] = array('show' => 'history.show'); $params['limits']['From'] = $this->dtTransformer->transform($limits['From']); $params['limits']['Until'] = $this->dtTransformer->transform($limits['Until']); } @@ -110,21 +110,20 @@ public function historyAction($ipAddress) */ public function statisticsByUserNameAction($username) { - if (strLen($username) > 25) { - throw new \BadMethodCallException('Username longer then 25 bytes'); - } + $this->initDateTimeTransformer(); $filtered = UsernamePasswordFormAuthenticationGuard::filterCredentials(array($username, '')); $username = $filtered[0]; $governor = $this->get('metaclass_auth_guard.tresholds_governor'); $params['routes']['this'] = 'Guard_statisticsByUserName'; $params['action_params'] = array('username' => $username); - $params['fieldSpec'] = array('Gebruikersnaam' => 'username'); + $params['title'] = $this->get('translator')->trans('history.title', array(), 'metaclass_auth_guard'); + $params['fieldSpec'] = array('secu_requests.username' => 'username'); $this->addStatisticCommonParams($params, $governor, 'Guard_statisticsByUserName'); - $params['fieldSpec']['gebruikersnaam op adres vrijgeven voor'] = 'allowReleasedUserOnAddressFor'; - $params['fieldSpec']['Gebruikersnaam geblokkeerd'] = 'usernameBlocked'; + $params['fieldSpec']['tresholds_governor_params.allowReleasedUserOnAddressFor'] = 'allowReleasedUserOnAddressFor'; + $params['fieldSpec']['statisticsByUserName.isUsernameBlocked'] = 'usernameBlocked'; $countingSince = new \DateTime("$governor->dtString - $governor->blockUsernamesFor"); $fieldValues =& $params['fieldValues']; @@ -133,21 +132,20 @@ public function statisticsByUserNameAction($username) $fieldValues['failureCount'] = $governor->requestCountsManager->countLoginsFailedForUserName($username, $countingSince); $fieldValues['successCount'] = $governor->requestCountsManager->countLoginsSucceededForUserName($username,$countingSince); $isUsernameBlocked = $fieldValues['failureCount'] >= $governor->limitPerUserName; - $fieldValues['usernameBlocked'] = $this->booleanLabels[$isUsernameBlocked]; - $fieldValues['allowReleasedUserOnAddressFor'] = $this->translatePeriod($governor->allowReleasedUserOnAddressFor); - + $fieldValues['usernameBlocked'] = $this->booleanLabel($isUsernameBlocked); + $fieldValues['allowReleasedUserOnAddressFor'] = $this->translateRelativeDate($governor->allowReleasedUserOnAddressFor); $limitFrom = $this->getRequest()->isMethod('POST') || $this->getRequest()->get('StatsPeriod') ? null : $countingSince; $limits = $this->addStatsPeriodForm($params, $governor, 'Historie', $limitFrom); if (isSet($limits['From'])) { - $params['labels'] = array('show' => 'Bekijken'); + $params['labels'] = array('show' => 'history.show'); $params['route_history'] = 'Guard_history'; $params['limits']['From'] = $this->dtTransformer->transform($limits['From']); $params['limits']['Until'] = $this->dtTransformer->transform($limits['Until']); $history = $governor->requestCountsManager->countsByUsernameBetween($username, $limits['From'], $limits['Until']); - $this->addHistoryTableParams($params, $history, 'ipAddress', 'Adres'); + $this->addHistoryTableParams($params, $history, 'ipAddress', 'secu_requests.col.ipAddress'); } // #TODO: make params testable @@ -159,14 +157,14 @@ public function statisticsByUserNameAction($username) protected function addHistoryTableParams(&$params, $history, $col1Field, $col1Label) { $params['columnSpec'] = array( - 'Vanaf' => 'dtFrom', + 'secu_requests.col.dtFrom' => 'dtFrom', $col1Label => $col1Field, - 'Succesvol' => 'loginsSucceeded', - 'Mislukt' => 'loginsFailed', - 'adres' => 'ipAddressBlocked', - 'naam' => 'usernameBlocked', - 'naam op adres' => 'usernameBlockedForIpAddress', - 'naam op cookie' => 'usernameBlockedForCookie', + 'secu_requests.col.loginsSucceeded' => 'loginsSucceeded', + 'secu_requests.col.loginsFailed' => 'loginsFailed', + 'secu_requests.col.ipAddressBlocked' => 'ipAddressBlocked', + 'secu_requests.col.usernameBlocked' => 'usernameBlocked', + 'secu_requests.col.usernameBlockedForIpAddress' => 'usernameBlockedForIpAddress', + 'secu_requests.col.usernameBlockedForCookie' => 'usernameBlockedForCookie', ); forEach($history as $key => $row) { $dt = new \DateTime($row['dtFrom']); @@ -179,12 +177,17 @@ protected function addHistoryTableParams(&$params, $history, $col1Field, $col1La protected function addStatsPeriodForm(&$params, $governor, $label, $limitFrom=null) { $limits['Until'] = new \DateTime(); - $labels = array('From' => 'Van', 'Until' => 'Tot'); + $labels = array('From' => 'StatsPeriod.From', 'Until' => 'StatsPeriod.Until'); $historyLimit = new \DateTime("$governor->dtString - $governor->keepCountsFor"); + + $formTypeClass = $this->container->getParameter('metaclass_auth_guard.statistics.StatsPeriod.formType'); + if (!class_exists($formTypeClass)) { + throw new RuntimeException("value of metaclass_auth_guard.statistics.StatsPeriod.formType is not a class: '$formTypeClass'"); + } $form = $this->createForm( - new StatsPeriodType($labels, $historyLimit, $this->dateTimeFormat), + new $formTypeClass($labels, $historyLimit, $this->dateFormat, $this->dateTimePattern), null, - array('label' => $label, 'csrf_protection' => false,)); + array('label' => $label, 'csrf_protection' => false,'translation_domain' => 'metaclass_auth_guard')); if ($limitFrom === null) { $form->submit($this->getRequest()); if ($form->isValid()) { @@ -202,21 +205,20 @@ protected function addStatsPeriodForm(&$params, $governor, $label, $limitFrom=nu protected function addStatisticCommonParams(&$params, $governor) { - $params['title'] = 'Inlogbeveiliging'; $this->buildMenu($params, 'Guard_show'); $fieldSpec = array( - 'Telt sinds' => 'countingSince', - 'Blokkeeer gebruikersnamen voor' => 'blockUsernamesFor', - 'Blokkeeer IP adressen voor' => 'blockIpAddressesFor', - 'Succesvolle inlogpogingen' => 'successCount', - 'Mislukte inlogpogingen' => 'failureCount', + 'tresholds_governor_params.countingSince' => 'countingSince', + 'tresholds_governor_params.blockUsernamesFor' => 'blockUsernamesFor', + 'tresholds_governor_params.blockIpAddressesFor' => 'blockIpAddressesFor', + 'secu_requests.loginsSucceeded' => 'successCount', + 'secu_requests.loginsFailed' => 'failureCount', ); $params['fieldSpec'] = isSet($params['fieldSpec']) ? array_merge($params['fieldSpec'], $fieldSpec) : $fieldSpec; - $fieldValues['blockIpAddressesFor'] = $this->translatePeriod($governor->blockIpAddressesFor); - $fieldValues['blockUsernamesFor'] = $this->translatePeriod($governor->blockUsernamesFor); + $fieldValues['blockIpAddressesFor'] = $this->translateRelativeDate($governor->blockIpAddressesFor); + $fieldValues['blockUsernamesFor'] = $this->translateRelativeDate($governor->blockUsernamesFor); $params['fieldValues'] = $fieldValues; } @@ -224,15 +226,15 @@ protected function addCountsGroupedTableParams(&$params, $governor, $limits) { $countsByIpAddress = $governor->requestCountsManager->countsGroupedByIpAddress($limits['From'], $limits['Until']); $params['columnSpec'] = array( - 'Adres' => 'ipAddress', - 'Blok' => 'blocked', - 'Namen' => 'usernames', - 'Succesvol' => 'loginsSucceeded', - 'Mislukt' => 'loginsFailed', - 'adres' => 'ipAddressBlocked', - 'naam' => 'usernameBlocked', - 'naam op adres' => 'usernameBlockedForIpAddress', - 'naam op cookie' => 'usernameBlockedForCookie', + 'secu_requests.col.ipAddress' => 'ipAddress', + 'countsGroupedByIpAddress.col.blocked' => 'blocked', + 'countsGroupedByIpAddress.col.usernames' => 'usernames', + 'secu_requests.col.loginsSucceeded' => 'loginsSucceeded', + 'secu_requests.col.loginsFailed' => 'loginsFailed', + 'secu_requests.col.ipAddressBlocked' => 'ipAddressBlocked', + 'secu_requests.col.usernameBlocked' => 'usernameBlocked', + 'secu_requests.col.usernameBlockedForIpAddress' => 'usernameBlockedForIpAddress', + 'secu_requests.col.usernameBlockedForCookie' => 'usernameBlockedForCookie', ); if ($this->getRequest()->isMethod('POST')) { unSet($params['columnSpec']['Blok']); @@ -240,32 +242,84 @@ protected function addCountsGroupedTableParams(&$params, $governor, $limits) forEach($countsByIpAddress as $key => $row) { $blocked = $row['loginsFailed'] >= $governor->limitBasePerIpAddress; - $countsByIpAddress[$key]['blocked'] = $this->booleanLabels[$blocked]; - //nog toe te voegen: aantal gebruikernamen gereleased, aantal gebruikersnamen geblokkeerd + $countsByIpAddress[$key]['blocked'] = $this->booleanLabel($blocked); + // Yet to be added: count usernames released, count usernames blocked } $params['items'] = $countsByIpAddress; } - protected function translatePeriod($durationString) + /** + * @param boolean $value + * @return string like Yes or No + */ + protected function booleanLabel($value) { - return str_replace( - array('minutes', 'hours', 'days'), - array('minuten', 'uren', 'dagen'), - $durationString); + $key = $value ? 'boolean.1' : 'boolean.0'; + return $this->get('translator')->trans($key, array(), 'metaclass_auth_guard'); + } + + protected function initDateTimeTransformer() + { + $this->initDateFormatAndPattern(); + $this->dtTransformer = new DateTimeToLocalizedStringTransformer( + null, + null, + $this->dateFormat, + DateTimeType::DEFAULT_TIME_FORMAT, // Compatible with DateTimeType + \IntlDateFormatter::GREGORIAN, + $this->dateTimePattern); } - protected function getDecisionLabel($rejection) + /** + * Derives $this->dateFormat and $this->dateTimePattern from + * parameter metaclass_auth_guard.ui.dateTimeFormat. + * If FULL, LONG, MEDIUM or SHORT (case independent) the corresponding + * dateformat is used. Otherwise the parameter is used as pattern. + * + * To be overridden by subclass if pattern depends on locale or varies otherwise + */ + protected function initDateFormatAndPattern() { - if (!$rejection) { - return ''; + $formatOption = $this->container->getParameter('metaclass_auth_guard.ui.dateTimeFormat'); + $constantOptions = array( + 'FULL' => \IntlDateFormatter::FULL, + 'LONG' => \IntlDateFormatter::LONG, + 'MEDIUM' => \IntlDateFormatter::MEDIUM, + 'SHORT' => \IntlDateFormatter::SHORT, + ); + $dateFormat = null; + $formatOptionUc = strtoupper($formatOption); + if ( isset($constantOptions[$formatOptionUc]) ) { + $this->dateFormat = $constantOptions[$formatOptionUc]; + $this->dateTimePattern = null; + } else { + $this->dateFormat = DateTimeType::DEFAULT_DATE_FORMAT; + $this->dateTimePattern = $formatOption; } - $fqClass = get_class($rejection); - $pos = strRpos($fqClass, '\\'); + } - return $this->rejectionLabels[subStr($fqClass, $pos + 1)]; + protected function translateRelativeDate($durationString) + { + $toTranslate = array('minutes', 'hours', 'days'); + $translated = $this->translateRelativeDateArray($toTranslate); + return str_replace( + $toTranslate, + $translated, + $durationString); } + protected function translateRelativeDateArray($toTranslate) + { + if (!isset($this->translateRelativeDateArray)) { + $t = $this->get('translator'); + $this->translateRelativeDateArray = array(); + foreach ($toTranslate as $name) { + $this->translateRelativeDateArray[] = $t->trans('relativeDate.'.$name, array(), 'metaclass_auth_guard'); + } + } + return $this->translateRelativeDateArray; + } protected function buildMenu(&$params, $currentRoute) { diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index fa2330b..21ca0d3 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -21,13 +21,22 @@ public function getConfigTreeBuilder() $rootNode = $treeBuilder->root('metaclass_authentication_guard'); $rootNode + ->addDefaultsIfNotSet() ->children() ->arrayNode('db_connection') + ->addDefaultsIfNotSet() ->children() ->scalarNode('name')->defaultValue('default')->end() ->end() ->end() + ->arrayNode('ui') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('dateTimeFormat')->defaultValue('SHORT')->end() + ->end() + ->end() ->arrayNode('tresholds_governor_params') + ->addDefaultsIfNotSet() ->children() ->scalarNode('counterDurationInSeconds')->defaultValue(180)->end() ->scalarNode('blockUsernamesFor')->defaultValue('25 minutes')->end() diff --git a/DependencyInjection/MetaclassAuthenticationGuardExtension.php b/DependencyInjection/MetaclassAuthenticationGuardExtension.php index d92528e..961c995 100644 --- a/DependencyInjection/MetaclassAuthenticationGuardExtension.php +++ b/DependencyInjection/MetaclassAuthenticationGuardExtension.php @@ -22,6 +22,7 @@ public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); $container->setParameter('metaclass_auth_guard.db_connection.name', $config['db_connection']['name']); + $container->setParameter('metaclass_auth_guard.ui.dateTimeFormat', $config['ui']['dateTimeFormat']); $container->setParameter('metaclass_auth_guard.tresholds_governor_params', $config['tresholds_governor_params']); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); diff --git a/Form/Type/StatsPeriodType.php b/Form/Type/StatsPeriodType.php index fe581df..14e23dd 100644 --- a/Form/Type/StatsPeriodType.php +++ b/Form/Type/StatsPeriodType.php @@ -11,11 +11,12 @@ class StatsPeriodType extends AbstractType { protected $labels; - public function __construct($labels, \DateTime $min, $dateTimeFormat) + public function __construct($labels, \DateTime $min, $dateFormat, $formatPattern) { $this->labels = $labels; $this->min = $min; - $this->dateTimeFormat = $dateTimeFormat; + $this->dateFormat = $dateFormat; + $this->formatPattern = $formatPattern; } public function buildForm(FormBuilderInterface $builder, array $options) @@ -27,14 +28,16 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'label' => $this->labels['From'], 'required' => true, 'widget' => 'single_text', - 'format' => $this->dateTimeFormat, + 'date_format' => $this->dateFormat, + 'format' => $this->formatPattern, 'constraints' => $constraints )); $builder->add('Until', 'datetime', array( 'label' => $this->labels['Until'], 'required' => false, 'widget' => 'single_text', - 'format' => $this->dateTimeFormat, + 'date_format' => $this->dateFormat, + 'format' => $this->formatPattern, 'constraints' => $constraints )); } diff --git a/README.md b/README.md index 49af1bb..1d23553 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,12 @@ FEATURES - Stores counters instead of individual requests to prevent database flooding from brute force attacks, +- Web based user interface for user administrators to look into why a user may have been blocked. + REQUIREMENTS ------------ -This bundle is for the symfony framework and requires Symfony >=2.3 and <=2.6.* +This is for the symfony framework and this version requires Symfony >=2.3 and <=2.6.*. +Another version will support Symfony >=2.6. Requires metaclass-nl/tresholds-governor 0.2@dev which uses Doctrine DBAL >=2.3 Tested with MySQL 5.5. @@ -43,13 +46,16 @@ login form to hide differences between them that should not be reported to users May be vurnerable to enumeration of usernames through timing attacks because of differences in database query performance for frequently and infrequently used usernames. -This can be mitigated by calling ::sleepUntilFixedExecutionTime. Under normal circomstances +This is mitigated by sleeping until a fixed execution time is reached. Under normal circomstances that should be sufficient if the fixedExecutionSeconds is set long enough, but under high (database) server loads when performance degrades, under specific conditions information may still be extractable by timing. Furthermore, the measures against timing attacks where not tested for practical effectiveness. -The web based administration user interface is experimental and requires doctrine/doctrine-bundle. +The web based administration user interface is experimental, requires doctrine/doctrine-bundle +and is currenly only in English and Dutch (Please clone me on Github and add your own language translation!). + +0.3.* versions are tested with Symfony 2.3 and 2.6. DOCUMENTATION ------------- diff --git a/Resources/config/services.yml b/Resources/config/services.yml index e5e76c8..a61b2a2 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -2,7 +2,8 @@ parameters: metaclass_auth_guard.manager.class: "Metaclass\TresholdsGovernor\Manager\RdbManager" metaclass_auth_guard.gateway.class: "Metaclass\TresholdsGovernor\Gateway\DbalGateway" metaclass_auth_guard.tresholds_governor.class: "Metaclass\TresholdsGovernor\Service\TresholdsGovernor" - metaclass_auth_guard.authentication.listener.form.class: Metaclass\AuthenticationGuardBundle\Service\UsernamePasswordFormAuthenticationGuard + metaclass_auth_guard.authentication.listener.form.class: "Metaclass\AuthenticationGuardBundle\Service\UsernamePasswordFormAuthenticationGuard" + metaclass_auth_guard.statistics.StatsPeriod.formType: "Metaclass\AuthenticationGuardBundle\Form\Type\StatsPeriodType" metaclass_auth_guard.statistics.template: "MetaclassAuthenticationGuardBundle:Guard:statistics.html.twig" services: diff --git a/Resources/doc/Installation.md b/Resources/doc/Installation.md index 1c47279..7468bd1 100644 --- a/Resources/doc/Installation.md +++ b/Resources/doc/Installation.md @@ -76,7 +76,7 @@ Installation - [setAuthExecutionSeconds, [0.99]] # voluntary ``` -7. You also need to add the following configuraton parameters (defaults shown): +7. You may also add the following configuraton parameters (defaults shown): ```yml metaclass_authentication_guard: @@ -93,6 +93,8 @@ Installation keepCountsFor: "4 days" fixedExecutionSeconds: "0.1" randomSleepingNanosecondsMax: 99999 + ui: + dateTimeFormat: "SHORT" ``` 8. From cron or so you may garbage-collect/pack stored RequestCounts: @@ -146,6 +148,12 @@ Installation in your applications routing.yml after the metaclass_auth_guard resource configuration or replace the resource configuration entirely. + If you want to use other datetime widgets you may override the parameter + metaclass_auth_guard.statistics.StatsPeriod.formType to refer to a class of your own. + + Currently the web based user interface only supports English and Dutch. + Please clone the Bundle on Github and add your own language translation! + 10. If you want to run the tests you may add the following to the testsuites section of your app/phpunit.xml: ```xml @@ -281,6 +289,24 @@ Configurations details a random between 0 and this value is added by ::sleepUntilSinceInit (which is called by ::sleepUntilFixedExecutionTime). +12. + ui: + dateTimeFormat + + \IntlDateFormatter pattern or datetype. If a dattype is set + (FULL, LONG, MEDIUM or SHORT) (case independent) the corresponding + dateformat is used and no pattern so that the formatting will depend + on the locale. Otherwise the parameter is used as pattern with + \Symfony\Component\Form\Extension\Core\Type\DateTimeType::DEFAULT_DATE_FORMAT + as datetype. As timetype DateTimeType::DEFAULT_TIME_FORMAT allways used so that + the formatting is the same as done by the DateTimeType widgets in the Period form. + + If you need specific patterns for different locales you may use your own subclass + of GuardStatsController and override ::initDateFormatAndPattern to set the appropriate + datetype and format, or override ::initDateTimeTransformer to set whatever + transformer you may like (but that will not be used by the DateTimeType widgets in the + Period form so you may want to set your own form type too). + Notes - releasing is possible for a username in general, an IP address in general, or for the combination of a username with an ip address diff --git a/Resources/doc/changelog.txt b/Resources/doc/changelog.txt index 1da49f1..a171d8b 100644 --- a/Resources/doc/changelog.txt +++ b/Resources/doc/changelog.txt @@ -270,17 +270,35 @@ created branche 0_3 (branche 0_3) - Corrected README.md (Requirements) - composer.json now requires symfony >=2.3.8,<=2.6.* +committed, pushed +------------------------------------------------ +(branche 0_3) +Issue #15: Remove hard-coded Dutch labels +- Controller\GuardStatsController replaced label values by translation keys + replaced options and their lookup by methods and calls + ::addStatsPeriodForm now passes datetype and datetime pattern to formtype + ::translateRelativeDate adapted + ::initDateFormatAndPattern, ::initDateTimeTransformer now use configuration parameter and support locale-dependent date formatting +- Form\Type\StatsPeriodType now expects and uses datetype and datetime pattern +- Resources/translations/metaclass_auth_guard.nl.yml added with Dutch translations +- Resources/translations/metaclass_auth_guard.en.yml added with English translations +- Resources/views/Guard/statistics_content.html.twig added trans calls +- DependencyInjection\Configuration::getConfigTreeBuilder added ui.dateTimeFormat +- DependencyInjection\MetaclassAuthenticationGuardExtension::load now sets param: + metaclass_auth_guard.ui.dateTimeFormat +- Readme.md restored the Web based user interface feature +- Resources/doc/Installation.md added doc on: + - Bundle parameter metaclass_auth_guard.ui.dateTimeFormat + - services parameter metaclass_auth_guard.statistics.StatsPeriod.formType + - Available translations (&clone me on Github) +Issue #17: Default configuration set without adding to config.yml +- DependencyInjection\Configuration::getConfigTreeBuilder added ->addDefaultsIfNotSet() to each arraynode -2DO: -ArboSDW verbeteren: -- normale hyperlinks onderscheiden, maar hyperlinks in menu grijs houden +Make web based user interface more extendable: +- Resources/config/services.yml added parameters metaclass_auth_guard.statistics.StatsPeriod.formType +- Controller\GuardStatsController::addStatsPeriodForm now uses parameter metaclass_auth_guard.statistics.StatsPeriod.formType + ::initDateFormatAndPattern may be overridden for setting locale dependent (custom) patterns -- tanslation +---------------------------------------------------- -- solve problem with params if not defined: -metaclass_authentication_guard: - db_connection: - name: "" - tresholds_governor_params: - counterDurationInSeconds: 300 diff --git a/Resources/translations/metaclass_auth_guard.en.yml b/Resources/translations/metaclass_auth_guard.en.yml new file mode 100644 index 0000000..9574781 --- /dev/null +++ b/Resources/translations/metaclass_auth_guard.en.yml @@ -0,0 +1,55 @@ +statistics: + title: Authentication Guard + +history: + title: Authentication history + show: Show authentication history + +statisticsByUserName: + isUsernameBlocked: Username blocked + +tresholds_governor_params: + countingSince: Counting since + blockUsernamesFor: Block usernames for + blockIpAddressesFor: Block IP addresses for + allowReleasedUserOnAddressFor: Release user on address for + limitPerUserName: Maximum per username + limitBasePerIpAddress: Maximum per IP adress + +secu_requests: + username: Username + loginsFailed: Failed logins + loginsSucceeded: Successfull logins + col: + dtFrom: From + username: Username + ipAddress: Address + loginsSucceeded: Succeeded + loginsFailed: Failed + ipAddressBlocked: address + usernameBlocked: name + usernameBlockedForIpAddress: name on addresd + usernameBlockedForCookie: name on cookie + blockedColumns: Number of attepmts blocked for + +countsGroupedByIpAddress: + col: + blocked: Blocked + usernames: Names + +StatsPeriod: + From: From + Until: Until + history: History + statistics: IP Adresses + submit: Show + +relativeDate: + minutes: minutes + hours: hours + days: days + +boolean: + 0: Yes + 1: No + diff --git a/Resources/translations/metaclass_auth_guard.nl.yml b/Resources/translations/metaclass_auth_guard.nl.yml new file mode 100644 index 0000000..9741792 --- /dev/null +++ b/Resources/translations/metaclass_auth_guard.nl.yml @@ -0,0 +1,55 @@ +statistics: + title: Inlogbeveiliging + +history: + title: Inloghistorie + show: Toon inloghistorie + +statisticsByUserName: + isUsernameBlocked: Gebruikersnaam geblokkeerd + +tresholds_governor_params: + countingSince: Telt sinds + blockUsernamesFor: Blokkeeer gebruikersnamen voor + blockIpAddressesFor: Blokkeeer IP adressen voor + allowReleasedUserOnAddressFor: Gebruikersnaam op adres vrijgeven voor + limitPerUserName: Maximum per gebruikersnaam + limitBasePerIpAddress: Maximum per Adres + +secu_requests: + username: Gebruikersnaam + loginsFailed: Mislukte inlogpogingen + loginsSucceeded: Succesvolle inlogpogingen + col: + dtFrom: Vanaf + username: Naam + ipAddress: Adres + loginsSucceeded: Succesvol + loginsFailed: Mislukt + ipAddressBlocked: adres + usernameBlocked: naam + usernameBlockedForIpAddress: naam op adres + usernameBlockedForCookie: naam op cookie + blockedColumns: Aantal pogingen geblokkeerd op + +countsGroupedByIpAddress: + col: + blocked: Blok + usernames: Namen + +StatsPeriod: + From: Van + Until: Tot + history: Historie + statistics: IP Adressen + submit: Tonen + +relativeDate: + minutes: minuten + hours: uren + days: dagen + +boolean: + 0: Nee + 1: Ja + diff --git a/Resources/views/Guard/statistics_content.html.twig b/Resources/views/Guard/statistics_content.html.twig index 4807ce4..106e373 100644 --- a/Resources/views/Guard/statistics_content.html.twig +++ b/Resources/views/Guard/statistics_content.html.twig @@ -4,7 +4,7 @@
{% for label, field in fieldSpec %}
- +
{{ fieldValues[field] }}
@@ -16,7 +16,7 @@ {% if form is defined %}
- {{ form.vars.label }} + {{ form.vars.label|trans({}, 'metaclass_auth_guard') }}
{% for eachError in form.vars.errors %}
{{ eachError.message }}
@@ -26,7 +26,7 @@ {{ include("MetaclassAuthenticationGuardBundle:Entity:editrow.html.twig") }} {% endfor %} - +
{% endif %} @@ -36,11 +36,11 @@   - Aantal pogingen geblokkeerd op + {{ 'secu_requests.blockedColumns'|trans({}, 'metaclass_auth_guard') }} {% for label, key in columnSpec %} - {{ label }} + {{ label|trans({}, 'metaclass_auth_guard') }} {% endfor %} @@ -49,10 +49,10 @@ {% for label, key in columnSpec %} {% if (key == 'ipAddress' and route_history is defined) %} - + {{ entity[key] }} {% elseif (key == 'username' and route_byUsername is defined) %} - + {{ entity[key] }} {% else %} {{ entity[key] }}