Silex Authentication - Restrict access after 3 missed attempts - silex

I'm new in Silex and I can not find how to change SecurityServiceProvider to restrict access for 24 hours after 3 bad connections.
The authentication works perfectly.
Thank you very much for your help and ideas.

You can create a CustomAuthenticationFailureHandler which extends the DefaultAuthenticationFailureHandler.
Create a class:
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
class CustomAuthenticationFailureHandler extends DefaultAuthenticationFailureHandler
{
/**
* (non-PHPdoc)
* #see \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler::onAuthenticationFailure()
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
// create a failure counter for the access restriction
return $this->httpUtils->createRedirectResponse($request, $this->options['failure_path']);
}
}
share this class:
$app['security.authentication.failure_handler.general'] = $app->share(function() use ($app) {
return new CustomAuthenticationFailureHandler($app['security.http_utils'], array(), $app);
});
where this failure handler is matching to the firewall named general:
// init the firewall
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'general' => array(
'pattern' => '^/',
'anonymous' => true,
'form' => array(
'login_path' => '/login',
'check_path' => '/admin/login_check'
),
...
)
)
);
you will also need a CustomAuthenticationSuccessHandler which extends the DefaultAuthenticationSuccessHandler:
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
class CustomAuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler
{
/**
* (non-PHPdoc)
* #see \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler::onAuthenticationSuccess()
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
// handle the 24 hour restriction for the user ...
return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
}
}
and share this class:
$app['security.authentication.success_handler.general'] = $app->share(function () use ($app) {
return new CustomAuthenticationSuccessHandler($app['security.http_utils'], array(), $app);
});
hope this helpful for you ...

Thanks to Ralf Hertsch for giving a clever answer, however as tekilatexee points, the Failure Handler does not accept an HttpUtils instance as first parameter to the constructor, if it extends DefaultAuthenticationFailureHandler without overriding the constructor.
Also, you don't need to write both Custom Handlers (one for failure, one for success), you could link only a Failure Handler, or only a Success Handler.
To instance correctly your Failure Handler extending the Symfony's default, the correct definition of the handler service is like this:
$app['security.authentication.failure_handler.general'] = $app->share(function() use ($app) {
return new CustomAuthenticationFailureHandler($app['kernel'], $app['security.http_utils']);
});
This passes the Symfony\Component\HttpKernel\HttpKernelInterface as first argument to the constructor.
However, if your Custom Handler instead of extending Symfony's default, implements Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface it just needs to implement onAuthenticationFailure that receives a Symfony's Request object and an AuthenticationException, and you could pass in the constructor, an instance of Doctrine\DBAL\Connection to log and check previous authentication failures, or any other custom set of services.

Related

ZF2 service manager use from a custom class

It looks like it has been touched several times already, but i still can't get it work. I set up an JSON-RPC server in a separate module, it works fine. Its functionality is in a new class Rpcapi. Now I want reuse DB related functions that already implemented in another module from that class. According to ZF2 docs my Rpcapi class has to be ServiceLocator-aware and it looks like I made it that way. Unfortunatelly still can't get it working. Please help keeping in mind that I'm new with ZF2 :)
Rpccontroller.php
namespace Rpc\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Json\Server\Server;
use Zend\Json\Server\Smd;
use Rpc\Model\Rpcapi;
class RpcController extends AbstractActionController
{
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass(new Rpcapi);
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
}
module.config.php for Rpc module
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
),
),
Rpcapi.php
namespace Rpc\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Rpcapi implements ServiceLocatorAwareInterface
{
protected $services;
protected $siteTable;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function getSiteTable()
{
if (!$this->siteTable) {
$sm = $this->getServiceLocator();
$this->siteTable = $sm->get('rpcapi');
}
return $this->siteTable;
}
/**
* Returns list of all sites
*
*
* #return array
*/
public function getAllSites()
{
$results = $this->getSiteTable()->fetchAll();
$r = array ('1' => '1', '2' => 2); //Just to return something for now
return $r;
}
}
All I could get out is: Fatal error: Call to a member function get() on a non-object in /var/www/html/AmeriFluxZF2/module/Rpc/src/Rpc/Model/Rpcapi.php on line 28. Line 28 is:
$this->siteTable = $sm->get('rpcapi');
Any help is much appreciated!
Making the class service locator aware tells the ZF2 that the service locator should be injected into your class upon instantiation. However, you still need to use the service locator to instantiate this class, rather than creating an instance of it yourself, or this will never happen.
Your probably want to add a new entry to invokables for your Rpcapi class, and then grab this from the service locator instead of doing new Rpcapi in your controller.
PS: The naming of your classes is very confusing - you have an Rpcapi class, and an invokable called rpcapi, yet this invokable creates an instance of a completely different class?
If you want serviceLocator to be injected by the service manager in your Rpcapi, you must get it via the service manager itself :
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
'Rpc\Model\Rpcapi' => 'Rpc\Model\Rpcapi',
),
),
the action :
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass($this->getServiceLocator()->get('Rpc\Model\Rpcapi'));
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
And this is where you can see that your 'rcpai' name for SiteTable is not a good choice... ;)

ZF2 How to get access to the service manager on a custom class that is no a controller, helper or service

guys,
At this point i am close to start pulling hair out of my head. I don't find a way to achieve this.
I have a custom class that belongs to a custom folder i created under my WebServices Module src folder. I need to be able to instantiate this class from inside another module/controller but when i do that and dump the services member it contains null.
How can i have the service manager accesible from inside my ApiAuthentication class.
Any help will be appreciated. Thanks
<?php
namespace WebServices\Services;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ApiAuthenticationService extends \Zend\Soap\Client implements ServiceLocatorAwareInterface{
public $services;
function __construct($options = null){
parent::__construct('http://tinysoa.local/soap/security/api_authentication?wsdl',$options);
}
public function setServiceLocator(ServiceLocatorInterface $locator)
{
$this->services = $locator;
}
public function getServiceLocator()
{
return $this->services;
}
}
When i call this from inside another module/controller it dumps a null value:
class IndexController extends AbstractActionController
{
public function indexAction()
{
$a = new \WebServices\Services\ApiAuthenticationService();
var_dump($a->services);
Responding with my own answer to add-on to Adrian's, and the question you asked in response.
If your service has dependencies of it's own, you just use a factory instead of going the invokable route.
Say your service needs a cache adapter and database adapter. Also imagine that it can optionally be configured with some other service (FooService, below):
<?php
public function getServiceConfig()
{
return array(
'factories' => array(
'my_service' => function($sm){
$cache = $sm->get('Cache');
$dbAdapter = $sm->get('DefaultDbAdapter');
$fooService = $sm->get('FooService');
// instantiate your service with required dependencies
$mySvc = new \My\Shiny\Service($cache, $dbAdapter);
// inject an optional dependency
$mySvc->setFooService($fooService);
// return your shiny new service
return $mySvc;
}
)
);
}
Side Note: It's generally bad design to inject the ServiceManager all over the place. You're better off managing your dependencies more explicitly, like above.
This stuff is covered quite well in the Quick Start, if you haven't already read that.
Register your Service in Service Config and access it through getServiceLocator() method in controller.
Module.php
public function getServiceConfig()
{
return array(
'invokables' => array(
'my_service' => 'WebServices\Services\ApiAuthenticationService'
)
);
}
Controller
public function indexAction()
{
$service = $this->getServiceLocator()->get('my_service');
}

ZF2 SM, Do I really need to

I currently have a ZF2 Module which needs access to three database tables. No other module needs access to these tables.
So my question is (performance in mind too, please) should I really add the factories to the Module.php like I have been doing:
/**
* Service Configuration
*
* #return array
*/
public function getServiceConfig()
{
return array(
'invokables' => array(
'Login\Service\Authenticate' => 'Login\Service\Authenticate',
'Login\Service\Oauth' => 'Login\Service\Oauth'
),
'factories' => array(
'Login\Form\Login' => function () {
$form = new Form\Login();
$form->setInputFilter(new FormFilter\Login());
return $form;
},
'Login\Model\GaclEmployeePermission' => function ($sm) {
return new Model\GaclEmployeePermission($sm->get('OTWebsoft\Db\Adapter\Master'), $sm->get('OTWebsoft\Db\Adapter\Slave'));
},
'Login\Model\Passport' => function ($sm) {
return new Model\Passport($sm->get('OTWebsoft\Db\Adapter\Master'), $sm->get('OTWebsoft\Db\Adapter\Slave'));
},
'Login\Model\PassportLog' => function ($sm) {
return new Model\PassportLog($sm->get('OTWebsoft\Db\Adapter\Master'), $sm->get('OTWebsoft\Db\Adapter\Slave'));
}
)
);
}
Then I have an abstract class which does:
/**
* Table GaclEmployeePermission
*
* #return \Login\Model\GaclEmployeePermission
*/
protected function getTableGaclEmployeePermission()
{
return $this->getServiceManager()->get('Login\Model\GaclEmployeePermission');
}
OR should I just remove the configuration from the Module.php and in my abstract class just do this:
/**
* Table GaclEmployeePermission
*
* #return \Login\Model\GaclEmployeePermission
*/
protected function getTableGaclEmployeePermission()
{
return new GaclEmployeePermission($this->getMasterAdapter(), $this->getSlaveAdapter());
}
Both seems to work exactly the same. But when it comes to performance, which would you recommend? Keeping in mind, these three tables do not need to be accessed from any other module but this one.
The main reason to use a service manager or dependency injection container is not that other services could access it. The main reason is to apply dependency injection and thereby you apply inversion of control. This ease the usage of your objects, allow you to swap implementations easily, enhance the ability to test the class and to keep the class responsible about the contained logic and not bothering its dependencies.
In both examples, you actually use the service manager but it's still the same pattern: the class (inside getTableGaclEmployeePermission()) decides what to get. Either via the service manager or via direct instantiation, that does not actually matter much.
By your module configuration I cannot grasp the layers you have in the application and what they do (esp. what's about that abstract class), but in general, you should inject it's dependencies:
abstract class SomeAbstract
{
protected $permission;
public function __construct(Permission $permission)
{
$this->permission = $permission;
}
}
class Permission
{
protected $master;
protected $slave;
public function __construct(TableAdapter $master, TableAdapter $slave = null)
{
$this->master = $master;
if (null !== $slave) {
$this->slave = $slave;
}
}
}
Then you create the service manager configuration to inject those dependencies for the services you define:
'factories' => array(
'Login\Model\Permission' => function ($sl) {
$master = $sl->get('My\MasterAdapter');
$slave = null;
if ($some_flag) {
$slave = $sl->get('My\SlaveAdapter');
}
return new Login\Model\Permission($master, $slave);
},
'Login\Some\ConcreteImplementation' => function ($sl) {
$permission = $sl->get('Login\Model\Permission');
return new Login\Some\ConcreteImplementation($permission);
}
),
Then you request Login\Some\ConcreteImplementation and all the injection ($master, $slave and $permission) is done for you, enabling all the benefits described above.

Zend Framework 2 Custom elements using ServiceManager not work

I want create a custom element and use the short name for add the element into Form, using the new ServiceManager tecnique for ZF2 V.2.1+
I am try to copy the same sample of the zend documentation step to step but it not works.
When I use the service writting the short name, it raises a exception because service not found:
Zend\ServiceManager\Exception\ServiceNotFoundException
File:
Zend\ServiceManager\ServiceManager.php:456
Message:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Test
I think I have all classes identically, see follows
This is my custom element:
namespace SecureDraw\Form\Element;
use Zend\Form\Element\Text;
class ProvaElement extends Text {
protected $hola;
public function hola(){
return 'hola';
}
}
This is my Module.php I have my invokable service be able to use short name:
class Module implements FormElementProviderInterface {
//Rest of class
public function getFormElementConfig() {
return array(
'invokables' => array(
'Test' => 'SecureDraw\Form\Element\ProvaElement'
)
);
}
}
In my form I use for add the element, the commented line works ok, but with short name not works:
$this->add(array(
'name' => 'prova',
//'type' => 'SecureDraw\Form\Element\ProvaElement',
'type' => 'Test', //Fail
));
In my action:
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('SecureDraw\Form\UserForm');
$prova = $form->get('prova');
echo $prova->hola();
The problem is that the elements created via FormElementManager have to be created into init method instead __Construct method how it can see in this page.
The zend documentation is badly explained
Workaround:
In your own module, create the following two files:
FormElementManagerConfig with the invokables short names of your custom form elements;
Subclass Form\Factory and override getFormElementManager and pass the config to the FormElementManager constructor;
You then use your own Factory to create your Form, like this (you can pass a very rudimentary, e.g. empty array, or a more or less full-fledged $spec to $factory->createForm()):
$factory = new Factory();
$spec = array();
$form = $factory->createForm($spec);
FormElementManagerConfig.php:
class FormElementManagerConfig implements ConfigInterface
{
protected $invokables = array(
'anything' => 'MyModule\Form\Element\Anything',
);
public function configureServiceManager(ServiceManager $serviceManager)
{
foreach ($this->invokables as $name => $service) {
$serviceManager->setInvokableClass($name, $service);
}
}
}
MyFactory.php:
class Factory extends \Zend\Form\Factory
{
public function getFormElementManager()
{
if ($this->formElementManager === null) {
$this->setFormElementManager(new FormElementManager(new FormElementManagerConfig()));
}
return $this->formElementManager;
}
}

Getting objectManager / serviceLocator in fieldset in ZF2

In order to get my object manager inside my fieldset's init() function I followed the docs
At first I found out that I had to change
public function setServiceLocator(ServiceLocator $sl)
to
public function setServiceLocator(ServiceLocatorInterface $sl)
otherwise I got an error:
setServiceLocator() must be compatible with that of Zend\ServiceManager\ServiceLocatorAwareInterface::setServiceLocator()
When calling $this->getServiceLocator() I get an instance of the FormManager.
Additionally calling $this->getServiceLocator()->getServiceLocator() returns NULL.
Since I am still new to DI I wonder if I am missing a place to inject?
Testing I switched from
$form = new MyForm();
to
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Application\Form\MyForm');
Since then I get this error:
exception 'Zend\Di\Exception\RuntimeException' with message 'Invalid instantiator of type "NULL" for "Zend\ServiceManager\ServiceLocatorInterface".'
An abstract factory could not create an instance of applicationformmyform(alias: Application\Form\MyForm).
Anyway reading some other threads using the ServiceLocator Awareness isn't recommended. Is using the FormElementProviderInterface the alternative?
I used the ServiceLocatorAwareInterface before in my classes like this:
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyClass implements ServiceLocatorAwareInterface
{
protected $services;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
and simply calling it per service locator in my c
$sm = $this->getServiceLocator();
$myClass = $sm->get('Application\Service\MyClass');
without having to set an DI. Is this necessary for Fieldsets / Form and where / how exactely?
I tried to inject my Form and Fieldset this way:
'service_manager' => array(
'factories' => array(
'Application\Form\CreateCostcenter' => function (\Zend\ServiceManager\ServiceLocatorInterface $sl) {
$form = new \Application\Form\CreateCostcenter();
$form->setServiceLocator($sl);
return $form;
},
'Application\Form\CostcenterFieldset' => function (\Zend\ServiceManager\ServiceLocatorInterface $sl) {
$fieldset = new \Application\Form\CostcenterFieldset();
$fieldset->setServiceLocator($sl);
return $fieldset;
},
),
),
The injection works for my form when calling it in my controller:
$form = $this->getServiceLocator()->get('Application\Form\CreateCostcenter');
But of course it won't pass the serviceManager to the Fieldset.
Anyway I don't understand why there has to be a config for the serviceManagerAwareness since it works with other class by just implementing it.
There also is no hint in the docs for advanced usage since ZF 2.1:
use an initializer (like Zend\ServiceManager\ServiceLocatorAwareInterface) to inject a specific object to all your forms/fieldsets/elements

Resources