Override a complicated class with multiple parameters - php

I am trying to override the class UsernamePasswordFormAuthenticationListener.
parameters:
security.authentication.listener.form.class: AppBundle\Listener\LoginFormListener
class LoginFormListener extends UsernamePasswordFormAuthenticationListener
{
/**
* {#inheritdoc}
*/
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
{
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, $options, $logger, $dispatcher, $csrfProvider);
}
protected function attemptAuthentication(Request $request)
{
if (null !== $this->csrfTokenManager) {
$csrfToken = $request->get($this->options['csrf_parameter'], null, true);
if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
if ($this->options['post_only']) {
$username = trim($request->request->get($this->options['username_parameter'], null, true));
$password = $request->request->get($this->options['password_parameter'], null, true);
} else {
$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);
}
$request->getSession()->set(Security::LAST_USERNAME, $username);
$apiRequest = new ApiRequest();
$apiRequest->addMethod('login', array('email' => $username, 'password' => $password));
$response = $apiRequest->sendRequest();
dump($response);
exit;
}
}
But when I execute it, I have this error :
Catchable Fatal Error: Argument 1 passed to AppBundle\Listener\LoginFormListener::__construct() must implement interface Symfony\Component\Security\Core\SecurityContextInterface, instance of Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage given, called in /Users/dimitri/Sites/rennes/app/cache/dev/appDevDebugProjectContainer.php on line 4039 and defined
Any idea how I can make this work ?

You can simply change SecurityContextInterface type hint to TokenStorageInterface in your class and all should work fine - this class service has been changed recently (deprecated in 2.7) , so your example code might be outdated.
Check blog post entry to get more info about
SecurityContextInterface vs TokenStorageInterface

Related

Fatal error: Uncaught Error: Non-static method primaryKey() cannot be called statically [duplicate]

This question already has answers here:
Error message Strict standards: Non-static method should not be called statically in php
(7 answers)
Closed 1 year ago.
how can I fix this error?
Fatal error: Uncaught Error: Non-static method thecodeholic\phpmvc\db\DbModel::primaryKey() cannot be called statically in C:\xampp\htdocs\php-mvc-framework-master\vendor\thecodeholic\php-mvc-core\Application.php:53 Stack trace: #0 C:\xampp\htdocs\php-mvc-framework-master\public\index.php(25): thecodeholic\phpmvc\Application->__construct('C:\\xampp\\htdocs...', Array) #1 {main} thrown in C:\xampp\htdocs\php-mvc-framework-master\vendor\thecodeholic\php-mvc-core\Application.php on line 53
here is my index.php in public folder:
<?php
/**
* User: TheCodeholic
* Date: 7/7/2020
* Time: 9:57 AM
*/
use app\controllers\AboutController;
use app\controllers\SiteController;
use thecodeholic\phpmvc\Application;
require_once __DIR__ . '/../vendor/autoload.php';
$dotenv = \Dotenv\Dotenv::createImmutable(dirname(__DIR__));
$dotenv->load();
$config = [
'userClass' => \app\models\User::class,
'db' => [
'dsn' => $_ENV['DB_DSN'],
'user' => $_ENV['DB_USER'],
'password' => $_ENV['DB_PASSWORD'],
]
];
$app = new Application(dirname(__DIR__), $config);
$app->on(Application::EVENT_BEFORE_REQUEST, function(){
echo "Before request from second installation";
});
$app->router->get('/', [SiteController::class, 'home']);
...
$app->run();
and here is Application.php code:
<?php
/**
* User: TheCodeholic
* Date: 7/7/2020
* Time: 9:57 AM
*/
namespace thecodeholic\phpmvc;
use thecodeholic\phpmvc\db\Database;
/**
* Class Application
*
* #author Zura Sekhniashvili <zurasekhniashvili#gmail.com>
* #package app
*/
class Application
{
const EVENT_BEFORE_REQUEST = 'beforeRequest';
const EVENT_AFTER_REQUEST = 'afterRequest';
protected array $eventListeners = [];
public static Application $app;
public static string $ROOT_DIR;
public string $userClass;
public string $layout = 'main';
public Router $router;
public Request $request;
public Response $response;
public ?Controller $controller = null;
public Database $db;
public Session $session;
public View $view;
public ?UserModel $user;
public function __construct($rootDir, $config)
{
$this->user = null;
$this->userClass = $config['userClass'];
self::$ROOT_DIR = $rootDir;
self::$app = $this;
$this->request = new Request();
$this->response = new Response();
$this->router = new Router($this->request, $this->response);
$this->db = new Database($config['db']);
$this->session = new Session();
$this->view = new View();
$userId = Application::$app->session->get('user');
if ($userId) {
$key = $this->userClass::primaryKey();
$this->user = $this->userClass::findOne([$key => $userId]);
}
}
public static function isGuest()
{
return !self::$app->user;
}
public function login(UserModel $user)
{
$this->user = $user;
$primaryKey = $user->primaryKey();
$value = $user->{$primaryKey};
Application::$app->session->set('user', $value);
return true;
}
public function logout()
{
$this->user = null;
self::$app->session->remove('user');
}
public function run()
{
$this->triggerEvent(self::EVENT_BEFORE_REQUEST);
try {
echo $this->router->resolve();
} catch (\Exception $e) {
echo $this->router->renderView('_error', [
'exception' => $e,
]);
}
}
public function triggerEvent($eventName)
{
$callbacks = $this->eventListeners[$eventName] ?? [];
foreach ($callbacks as $callback) {
call_user_func($callback);
}
}
public function on($eventName, $callback)
{
$this->eventListeners[$eventName][] = $callback;
}
}
$key = $this->userClass->primaryKey();
$this->user = $this->userClass->findOne([$key => $userId]);
i think the function primaryKey and findOne will be normal public and not static in your Entity.
class UserClass {
public function primaryKey() {
....
}
public function findOne() {
...
}
}
so you has to use -> instead of :: to call the methods

Detect if there is an exception ( 404, 500, ....) before sending response in symfony php

I'm developing an application with symfony3, in which I'm tracking routes and saving them into my db with a service. This service is saving also a null route when there is an exception ( 500 , 404 ... ) so I want to add something to avoid saving this.
This is my service:
<?php
namespace Alpha\VisitorTrackingBundle\EventListener;
use Alpha\VisitorTrackingBundle\Entity\Lifetime;
use Alpha\VisitorTrackingBundle\Entity\PageView;
use Alpha\VisitorTrackingBundle\Entity\Session;
use Doctrine\ORM\EntityManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Class VisitorTrackingSubscriber
* #package Alpha\VisitorTrackingBundle\EventListener
*
* Tracks the source of a session and each pageview in that session. Primary use of this is to be able to see the path through our site each
*/
class VisitorTrackingSubscriber implements EventSubscriberInterface
{
/**
* #var \Doctrine\ORM\EntityManager
*/
protected $em;
/**
* #var Session
*/
protected $session;
/**
* #var Lifetime
*/
protected $lifetime;
protected $routesExclues;
protected $utmCodes = [
"utm_source",
"utm_medium",
"utm_campaign",
"utm_term",
"utm_content"
];
const COOKIE_LIFETIME = "lifetime";
const COOKIE_SESSION = "session";
public function __construct(EntityManager $em, $routesExclues)
{
$this->em = $em;
$this->routesExclues = $routesExclues;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (substr($request->get("_route"), 0, 1) == "_" ) {
//these are requests for assets/symfony toolbar etc. Not relevant for our tracking
return;
}
if ($request->cookies->has(self::COOKIE_SESSION) and !$this->requestHasUTMParameters($request)) {
$this->session = $this->em->getRepository("AlphaVisitorTrackingBundle:Session")->find($request->cookies->get(self::COOKIE_SESSION));
if ($this->session instanceof Session) {
$this->lifetime = $this->session->getLifetime();
} else {
$this->generateSessionAndLifetime($request);
}
} else {
$this->generateSessionAndLifetime($request);
}
}
public function onKernelResponse(FilterResponseEvent $event)
{
if (false === $this->session instanceof Session) {
return;
}
$request = $event->getRequest();
$response = $event->getResponse();
$exception = $event->getKernel()->handle($request)->getStatusCode();
if (substr($request->get("_route"), 0, 1) == "_" or ($response instanceof RedirectResponse) ) {
//these are requests for assets/symfony toolbar etc. Not relevant for our tracking
return;
}
$pageView = new PageView();
$route = $request->get('_route');
$pageView->setUrl($route);
if (strpos($request->getRequestUri(), $this->routesExclues) === false // && $response->getStatusCode() === '200'
)
$this->session->addPageView($pageView);
$this->em->flush($this->session);
if ($this->requestHasUTMParameters($request)) {
$this->setUTMSessionCookies($request, $response);
}
if (!$request->cookies->has(self::COOKIE_LIFETIME)) {
$response->headers->setCookie(new Cookie(self::COOKIE_LIFETIME, $this->lifetime->getId(), 0, "/", null, false, false));
}
//no session cookie set OR session cookie value != current session ID
if (!$request->cookies->has(self::COOKIE_SESSION) or ($request->cookies->get(self::COOKIE_SESSION) != $this->session->getId())) {
$response->headers->setCookie(new Cookie(self::COOKIE_SESSION, $this->session->getId(), 0, "/", null, false, false));
}
}
protected function requestHasUTMParameters(Request $request)
{
foreach ($this->utmCodes as $code) {
if ($request->query->has($code)) {
return true;
}
}
return false;
}
protected function setUTMSessionCookies(Request $request, Response $response)
{
foreach ($this->utmCodes as $code) {
$response->headers->clearCookie($code);
if ($request->query->has($code)) {
$response->headers->setCookie(new Cookie($code, $request->query->get($code), 0, "/", null, false, false));
}
}
}
/**
* #return Session
*/
public function getSession()
{
return $this->session;
}
/**
* #return Lifetime
*/
public function getLifetime()
{
return $this->lifetime;
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::RESPONSE => ['onKernelResponse', 1024],
KernelEvents::REQUEST => ["onKernelRequest", 16]
);
}
private function generateSessionAndLifetime(Request $request)
{
if (strpos($request->headers->get("User-Agent"), "bot") === false && $request->getClientIp() !== "::1"
) {
$lifetime = false;
if ($request->cookies->has(self::COOKIE_LIFETIME)) {
$lifetime = $this->em->getRepository("AlphaVisitorTrackingBundle:Lifetime")->find($request->cookies->get(self::COOKIE_LIFETIME));
}
if (!$lifetime) {
$lifetime = new Lifetime();
$this->em->persist($lifetime);
}
$session = new Session();
$session->setIp($request->getClientIp() ?: "");
$session->setReferrer($request->headers->get("Referer") ?: "");
$session->setUserAgent($request->headers->get("User-Agent") ?: "");
$session->setQueryString($request->getQueryString() ?: "");
$session->setLoanTerm($request->query->get("y") ?: "");
$session->setRepApr($request->query->has("r") ? hexdec($request->query->get("r")) / 100 : "");
$details = json_decode(file_get_contents("http://ipinfo.io/{$request->getClientIp()}/json"));
$names = json_decode(file_get_contents("http://country.io/names.json"), true);
$session->setPays($names[$details->country]);
foreach ($this->utmCodes as $code) {
$method = "set" . \Doctrine\Common\Inflector\Inflector::classify($code);
$session->$method($request->query->get($code) ?: "");
}
$lifetime->addSession($session);
$this->em->flush();
$this->session = $session;
$this->lifetime = $lifetime;
}
}
}
I have added something to get statusCode from response but it always
returns 200. ( if statusCode !== '200' it must not save route).
Anyone to help me to get an idea or a solution to my problem? Thanks

ObjectNormalizer overrides RelationshipNormalizer causing code to crash, why?

I am trying to add my own normalizer since I need to convert (denormalize) some raw values to it's related entities. This is what I have done:
namespace MMI\IntegrationBundle\Serializer\Normalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class RelationshipNormalizer implements NormalizerInterface, DenormalizerInterface
{
public function normalize($object, $format = null, array $context = [])
{
// #TODO implement this method
}
public function supportsNormalization($data, $format = null): bool
{
return $data instanceof \AgreementType;
}
public function denormalize($data, $class, $format = null, array $context = [])
{
// #TODO implement this method
}
public function supportsDenormalization($data, $type, $format = null): bool
{
$supportedTypes = [
\AgreementType::class => true
];
return isset($supportedTypes[$type]);
}
}
And this is how I am using it from the controller:
$propertyNameConverter = new PropertyNameConverter();
$encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer(
null,
$propertyNameConverter,
null,
new ReflectionExtractor()
);
$serializer = new Serializer([
new DateTimeNormalizer(),
new RelationshipNormalizer(),
$normalizer,
new ArrayDenormalizer(),
], [$encoder]);
When the code reach this method:
private function getNormalizer($data, $format, array $context)
{
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format, $context)) {
return $normalizer;
}
}
}
Using Xdebug and the IDE I can see how the condition $data instanceof \AgreementType is accomplish but then the code try again to check the Normalizer and then this function is executed:
public function supportsDenormalization($data, $type, $format = null)
{
return class_exists($type);
}
And that's exactly where I get the wrong normalizer causing the following error:
Notice: Uninitialized string offset: 0 in
vendor/symfony/symfony/src/Symfony/Component/Inflector/Inflector.php
at line 179
UPDATE:
I have tried this other way and result is exactly the same as before meaning same error message:
$callback = function ($value) {
$value = $this->em->getRepository('QuoteBundle:' . $this->table_mapping[$this->entity])->find($value);
return $value;
};
$entityNormalizer = new GetSetMethodNormalizer();
$entityNormalizer->setCallbacks([
'agreementType' => $callback,
]);
$serializer = new Serializer([
new DateTimeNormalizer(),
$normalizer,
$entityNormalizer,
new ArrayDenormalizer(),
], [$encoder]);
What I am missing here?
After get some help on #symfony-devs channel on Slack I did found that order matters. Here is the solution to my issue (compare the piece of code below to the one on the OP and you'll see the difference):
$normalizer = new ObjectNormalizer(
null,
$propertyNameConverter,
null,
new ReflectionExtractor()
);
// Notice how ObjectNormalizer() is the last normalizer
$serializer = new Serializer([
new ArrayDenormalizer(),
new DateTimeNormalizer(),
new RelationshipNormalizer($em),
$normalizer,
], [$encoder]);

How to build an ACL Assertion for a variable value in Zend Framework 2?

I have a simple ACL configures in an acl.global.php like this:
return [
'acl' => [
'roles' => [
'guest' => null,
'member' => 'guest',
'admin' => 'member'
],
'resources' => [
'allow' => [
'Application\Controller\Index' => ['all' => 'member'],
'Application\Controller\Error' => ['all' => 'member'],
'Item\Controller\Process' => [
'index' => 'member',
'create' => 'member',
'showItem' => 'member', // website.tld/item/:id
'showList' => 'member' // website.tld/list-items
]
]
],
]
];
A parser iterates through the configuration and generates from the array elements calls to Zend\Permissions\Acl#allow(...) like $this->allow($role, $controller, $action);.
Now I need additionally to restrict the access of the users to the item's single view (mydomain.tld/item/:id). A user should only get the access, if its id equals to the item.user_id (means: the user is the author/owner).
The way I see to implement this requirement is to extend the config
'Item\Controller\Process' => [
'index' => 'member',
'create' => 'member',
'showItem' => [
'role' => 'member',
'assertion' => 'UserIsOwner'
]
'showList' => 'member'
]
and to inject the Assertion to Zend\Permissions\Acl#allow(...): $this->allow($role, $controller, $action, $assertion);.
namespace Authorization\Acl\Assertion;
use ...
class UserIsOwner implements AssertionInterface
{
protected $userId;
// To inject the $userId can be the job of the factory.
public function __construct(int $userId)
{
$this->userId = $userId;
}
public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null)
{
return return $this->userId === ???;
}
}
But now I have no idea, how the assertion should get the item.user_id injected. The example in the docu doesn't have this problem, since it assets against the $_SERVER['REMOTE_ADDR'].
I can inject the ItemService to find out the item.user_id:
public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null)
{
return $this->isUserOwner();
}
protected function isUserOwner()
{
$itemId = ???;
$item = $this->itemService->findOne($itemId);
$itemOwnerId = $item->getUser()->getId();
return $this->userId == $itemOwnerId;
}
Though then I still need external data -- the current item.id.
At what place can/should the variable item's data (in this case the item.user_id or item.id) be injected to an assertion?
Finally I resolved the problem by injecting the variable data via the resource. Don't think, that it's the cleanest or a recommended solution. Anyway it works. But it would be nice to know, how to resolve it a clean / more elegant way.
UserIsOwner
namespace Authorization\Acl\Assertion;
use Zend\Permissions\Acl\Assertion\AssertionInterface;
use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\RoleInterface;
use Zend\Permissions\Acl\Resource\ResourceInterface;
use Item\Service\ItemService;
class UserIsOwner implements AssertionInterface
{
/**
*
* #var integer
*/
protected $userId;
/**
*
* #var ItemService
*/
protected $itemService;
public function __construct(int $userId, ItemService $itemService)
{
$this->userId = $userId;
$this->itemService = $itemService;
}
public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null)
{
return isset($resource->getParams()['id']) ? $this->isUserOwner($resource->getParams()['id']) : false;
}
protected function isUserOwner($itemId)
{
$item = $this->itemService->findOne($itemId);
$itemOwnerId = $item->getUser()->getId();
return $this->userId == $itemOwnerId;
}
}
UserIsOwnerFactory
namespace Authorization\Acl\Assertion\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Authorization\Acl\Assertion\UserIsOwner;
class UserIsOwnerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$itemFieldsetService = $serviceLocator->get('Item\Service\ItemService');
$authenticationService = $serviceLocator->get('AuthenticationService');
$userId = !empty($authenticationService->getIdentity()['id']) ? $authenticationService->getIdentity()['id'] : null;
$service = new UserIsOwner($userId, $itemFieldsetService);
return $service;
}
}
ParametrizedResource
namespace Authorization\Acl\Resource;
use Zend\Permissions\Acl\Resource\GenericResource;
use Zend\Mvc\Router\Http\RouteMatch;
class ParametrizedResource extends GenericResource
{
/**
* #var array Params. Here the RouteMatch#params.
* #see RouteMatch
*/
protected $params;
public function __construct($resourceId, array $params = [])
{
parent::__construct($resourceId);
$this->setParams($params);
}
/**
*
* #return the $params
*/
public function getParams()
{
return $this->params;
}
/**
*
* #param multitype: $params
*/
public function setParams($params)
{
$this->params = $params;
}
}
Acl
...
// #todo refactor
protected function addResources(array $resources)
{
foreach ($resources as $permission => $controllers) {
foreach ($controllers as $controller => $actions) {
if ($controller == 'all') {
$controller = null;
} else {
if (! $this->hasResource($controller)) {
$this->addResource(new Resource($controller, $this->routeMatchParams));
}
}
foreach ($actions as $action => $roleConfig) {
if (is_array($roleConfig)) {
foreach ($roleConfig as $role => $assertion) {
if ($action == 'all') {
$action = null;
}
$assertion = !empty($this->assertions[$assertion]) ? $this->assertions[$assertion] : null;
if ($permission == 'allow') {
$this->allow($role, $controller, $action, $assertion);
} elseif ($permission == 'deny') {
$this->deny($role, $controller, $action, $assertion);
} else {
throw new \Exception('No valid permission defined: ' . $permission);
}
}
} elseif (is_string($roleConfig)) {
if ($action == 'all') {
$action = null;
}
if ($permission == 'allow') {
$this->allow($roleConfig, $controller, $action);
} elseif ($permission == 'deny') {
$this->deny($roleConfig, $controller, $action);
} else {
throw new \Exception('No valid permission defined: ' . $permission);
}
}
}
}
}
return $this;
}
...
AclFactory
namespace Authorization\Acl\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Authorization\Acl\Acl;
class AclFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('Config');
$assertions = [
'UserIsOwner' => $serviceLocator->get('Assertion\UserIsOwner')
];
$routeMatch = $serviceLocator->get('Application')->getMvcEvent()->getRouteMatch();
$routeMatchParams = $routeMatch->getParams();
$service = new Acl($config, $assertions, $routeMatchParams);
return $service;
}
}
I don't know if you can apply my solution because I configure my Acl in a AclService Class which wraps the Zend\Permission\Acl.
In this AclService I have defined a $assertions variable, which is an array which stores an object of every assertion that I have to use.
namespace User\Service;
use Zend\Permissions\Acl\Role\GenericRole as Role;
use Zend\Permissions\Acl\Resource\GenericResource as Resource;
use Zend\Permissions\Acl\Acl;
use User\Service\Assertion\RightLeagueAssertion;
use User\Service\Assertion\RightLeagueTeamAssertion;
class AclService {
const ROLE_GUEST = 'guest';
const ROLE_MEMBER = 'member';
const ROLE_COMISSIONER = 'comissioner';
const ROLE_ADMIN = 'admin';
const ROLE_GOD = 'god';
const ASSERTION_RIGHT_LEAGUE_TEAM = 'RightLeagueTeamAssertion';
protected $acl = null;
protected $assertions;
/**
* Constructor
*
* #param Acl $acl
* #return void
* #throws \Exception
*/
public function __construct($acl)
{
$this->acl = $acl;
$this->assertions[self::ASSERTION_RIGHT_LEAGUE_TEAM] = $rightLeagueTeam;
/* Declaramos los roles */
$this->acl->addRole(new Role(self::ROLE_GUEST));
$this->acl->addRole(new Role(self::ROLE_MEMBER), self::ROLE_GUEST);
$this->acl->addRole(new Role(self::ROLE_COMISSIONER), self::ROLE_MEMBER);
$this->acl->addRole(new Role(self::ROLE_ADMIN), self::ROLE_MEMBER);
//unique role for superadmin
$this->acl->addRole(new Role(self::ROLE_GOD));
/* Declaramos los recursos (module:controller) */
$this->acl->addResource(new Resource('application:index'));
$this->acl->addResource(new Resource('application:error'));
$this->acl->addResource(new Resource('user:user'));
$this->acl->addResource(new Resource('leueroneyear:league'));
$this->acl->addResource(new Resource('leueroneyear:team'));
/*** Permisos ***/
//'God' tiene permiso para todo
$this->acl->allow(self::ROLE_GOD);
//Una persona no logueada podrá ver solo el índice, errores, darse de alta y recuperar el password
$this->acl->allow(self::ROLE_GUEST, 'application:index', 'index');
$this->acl->allow(self::ROLE_GUEST, 'user:user', array('register','forgotpassword','resetpassword','login'));
$this->acl->allow(self::ROLE_GUEST, 'application:error');
$this->acl->allow(self::ROLE_GUEST, 'nba:test');
//Los usuarios sí que podrán visitar las páginas
$this->acl->allow(self::ROLE_MEMBER, 'user:user', array('get','edit', 'logout'));
$this->acl->allow(self::ROLE_MEMBER, 'leueroneyear:league', array('index','get','list','add','enter'));
$this->acl->allow(self::ROLE_MEMBER, 'leueroneyear:team', array('get','add'));
$this->acl->allow(self::ROLE_MEMBER, 'leueroneyear:team', 'index',$this->assertions[self::ASSERTION_RIGHT_LEAGUE_TEAM]);
}
public function getAcl()
{
return $this->acl;
}
public function isAllowed($role, $controller, $action)
{
$a = explode("\\",$controller);
$resource = strtolower($a[0]).":".strtolower($a[2]);
//\Zend\Debug\Debug::dump($resource); die();
return $this->acl->isAllowed($role, $resource, $action);
}
public function setRequestParams($params)
{
$a = explode("\\",$params["controller"]);
$controller = strtolower($a[2]);
switch ($controller) {
case 'team': $this->assertions[self::ASSERTION_RIGHT_LEAGUE_TEAM]->setRequestParams($params);
break;
}
}
}
When is time to check if somebody is allowed to use a resource, I inject the parameters of the route matched in the AclService which injects them in every assertion class previously instantiated (function 'setRequestParams').
/**
* #param MvcEvent $e
*/
public function onRoute(MvcEvent $event)
{
$matches = $event->getRouteMatch();
$controller = $matches->getParam('controller');
$action = $matches->getParam('action','index');
$auth = $this->authService;
/* #var $user User\Entity\User */
if ($user = $auth->getIdentity()) {
$session = new Container("League");
if (isset($session->isCommissioner) && $session->isCommissioner)
$role = AclService::ROLE_COMISSIONER;
else
$role = AclService::ROLE_MEMBER;
} else {
$role = AclService::ROLE_GUEST;
}
$acl = $this->aclService;
$acl->setRequestParams($matches->getParams());
if (!$acl->isAllowed($role,$controller, $action)) {
//El usuario no tiene los permisos necesarios
$app = $event->getTarget();
$route = $event->getRouteMatch();
$event -> setError(RouteGuard::ERROR)
-> setParam('route', $route->getMatchedRouteName());
$app->getEventManager()->trigger('dispatch.error', $event);
}
}
In this way, you can access these parameters in your assertion classes.
class RightLeagueTeamAssertion implements AssertionInterface
{
protected $requestParams;
public function setRequestParams($params)
{
$this->requestParams = $params;
}
/**
* Comprueba que el idTeam que pasan por parámetro pertenece a la liga en la que estás logueado
*/
public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) {
$appSession = new Container("Application");
$leagueSession = new Container("League");
$idLeague = $leagueSession->idLeague;
$idTeam = $this->requestParams['id'];
\Zend\Debug\Debug::dump($idTeam);
return false;
}
}
If you can not apply this solution directly, I hope it can point you to the right direction.

symfony2 login using 3 parameters (username, password and company)

I have been giving myself a headache over this all day,
I've been learning Symfony over the past couple days and my problem is simple, I need to be able to log in with 3 parameters (username, password and company)
The same question has been asked here, however the asker never provided a solution:
Custom Login using a Third Parameter
I have tried to start by cloning Symfony's inbuilt login by implementing a Token, Listener, Provider and Factory but whenever I insert my key into security.yml and try to log in it takes me to /login (No route found for "GET /login")
Anyone care to make my day?
EDIT:
OK, I Just tried again and now I have this error:
Catchable Fatal Error: "Argument 1 passed to Test\SampleBundle\Security\Authentication\Provider\MyProvider::__construct() must be an instance of Symfony\Component\Security\Core\User\UserCheckerInterface, instance of Symfony\Bridge\Doctrine\Security\User\EntityUserProvider given"
Again all I'm trying to do is duplicate Symfonys current user/password login functionality
MyToken.php:
namespace Test\SampleBundle\Security\Authentication\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
class MyToken extends AbstractToken
{
private $credentials;
private $providerKey;
public function __construct($user, $credentials, $providerKey, array $roles = array())
{
parent::__construct($roles);
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
}
$this->setUser($user);
$this->credentials = $credentials;
$this->providerKey = $providerKey;
parent::setAuthenticated(count($roles) > 0);
}
public function setAuthenticated($isAuthenticated)
{
if ($isAuthenticated) {
throw new \LogicException('Cannot set this token to trusted after instantiation.');
}
parent::setAuthenticated(false);
}
public function getCredentials()
{
return $this->credentials;
}
public function getProviderKey()
{
return $this->providerKey;
}
public function eraseCredentials()
{
parent::eraseCredentials();
$this->credentials = null;
}
public function serialize()
{
return serialize(array($this->credentials, $this->providerKey, parent::serialize()));
}
public function unserialize($serialized)
{
list($this->credentials, $this->providerKey, $parentStr) = unserialize($serialized);
parent::unserialize($parentStr);
}
}
MyListener.php:
<?php
namespace Test\SampleBundle\Security\Firewall;
use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Test\SampleBundle\Security\Authentication\Token\MyToken;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class MyListener extends AbstractAuthenticationListener
{
private $csrfProvider;
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
{
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
'username_parameter' => '_username' . 'b',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'intention' => 'authenticate',
'post_only' => true,
), $options), $logger, $dispatcher);
$this->csrfProvider = $csrfProvider;
}
protected function requiresAuthentication(Request $request)
{
if ($this->options['post_only'] && !$request->isMethod('POST')) {
return false;
}
return parent::requiresAuthentication($request);
}
protected function attemptAuthentication(Request $request)
{
if (null !== $this->csrfProvider) {
$csrfToken = $request->get($this->options['csrf_parameter'], null, true);
if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
if ($this->options['post_only']) {
$username = trim($request->request->get($this->options['username_parameter'], null, true));
$password = $request->request->get($this->options['password_parameter'], null, true);
} else {
$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);
}
$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
return $this->authenticationManager->authenticate(new MyToken($username, $password, $this->providerKey));
}
}
MyProivder.php:
namespace Test\SampleBundle\Security\Authentication\Provider;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Test\SampleBundle\Security\Authentication\Token\MyToken;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
class MyProvider implements AuthenticationProviderInterface
{
private $hideUserNotFoundExceptions;
private $userChecker;
private $providerKey;
public function __construct(UserCheckerInterface $userChecker, $providerKey, $hideUserNotFoundExceptions = true)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
}
$this->userChecker = $userChecker;
$this->providerKey = $providerKey;
$this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions;
}
public function authenticate(TokenInterface $token)
{
if (!$this->supports($token)) {
return null;
}
$username = $token->getUsername();
if (empty($username)) {
$username = 'NONE_PROVIDED';
}
try {
$user = $this->retrieveUser($username, $token);
} catch (UsernameNotFoundException $notFound) {
if ($this->hideUserNotFoundExceptions) {
throw new BadCredentialsException('Bad credentials', 0, $notFound);
}
$notFound->setUsername($username);
throw $notFound;
}
if (!$user instanceof UserInterface) {
throw new AuthenticationServiceException('retrieveUser() must return a UserInterface.');
}
try {
$this->userChecker->checkPreAuth($user);
$this->checkAuthentication($user, $token);
$this->userChecker->checkPostAuth($user);
} catch (BadCredentialsException $e) {
if ($this->hideUserNotFoundExceptions) {
throw new BadCredentialsException('Bad credentials', 0, $e);
}
throw $e;
}
$authenticatedToken = new MyToken($user, $token->getCredentials(), $this->providerKey, $user->getRoles());
$authenticatedToken->setAttributes($token->getAttributes());
return $authenticatedToken;
}
public function supports(TokenInterface $token)
{
return $token instanceof MyToken && $this->providerKey === $token->getProviderKey();
}
/**
* {#inheritdoc}
*/
protected function checkAuthentication(UserInterface $user, MyToken $token)
{
$currentUser = $token->getUser();
if ($currentUser instanceof UserInterface) {
if ($currentUser->getPassword() !== $user->getPassword()) {
throw new BadCredentialsException('The credentials were changed from another session.');
}
} else {
if ("" === ($presentedPassword = $token->getCredentials())) {
throw new BadCredentialsException('The presented password cannot be empty.');
}
if (!$this->encoderFactory->getEncoder($user)->isPasswordValid($user->getPassword(), $presentedPassword, $user->getSalt())) {
throw new BadCredentialsException('The presented password is invalid.');
}
}
}
/**
* {#inheritdoc}
*/
protected function retrieveUser($username, MyToken $token)
{
$user = $token->getUser();
if ($user instanceof UserInterface) {
return $user;
}
try {
$user = $this->userProvider->loadUserByUsername($username);
if (!$user instanceof UserInterface) {
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
}
return $user;
} catch (UsernameNotFoundException $notFound) {
$notFound->setUsername($username);
throw $notFound;
} catch (\Exception $repositoryProblem) {
$ex = new AuthenticationServiceException($repositoryProblem->getMessage(), 0, $repositoryProblem);
$ex->setToken($token);
throw $ex;
}
}
}
MyFactory.php
<?php
namespace Test\SampleBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
class MyFactory implements SecurityFactoryInterface
{
public function __construct()
{
$this->addOption('username_parameter', '_username');
$this->addOption('password_parameter', '_password');
$this->addOption('csrf_parameter', '_csrf_token');
$this->addOption('intention', 'authenticate');
$this->addOption('post_only', true);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'mylogin';
}
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.mylogin.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('mylogin.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider))
;
$listenerId = 'security.authentication.listener.mylogin.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('mylogin.security.authentication.listener'));
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function addConfiguration(NodeDefinition $node)
{
}
protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
{
$entryPointId = 'security.authentication.form_entry_point.'.$id;
$container
->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.form_entry_point'))
->addArgument(new Reference('security.http_utils'))
->addArgument($config['login_path'])
->addArgument($config['use_forward'])
;
return $entryPointId;
}
final public function addOption($name, $default = null)
{
$this->options[$name] = $default;
}
}
Services.yml:
services:
mylogin.security.authentication.provider:
class: Test\SampleBundle\Security\Authentication\Provider\MyProvider
arguments: ['', %kernel.cache_dir%/security/nonces]
mylogin.security.authentication.listener:
class: Test\SampleBundle\Security\Firewall\MyListener
arguments: [#security.context, #security.authentication.manager]
And finally injector:
namespace Test\SampleBundle;
use Test\SampleBundle\DependencyInjection\Security\Factory\MyFactory;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class TestSampleBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new MyFactory());
}
}
There may be a few errors in the above code as I have been playing around with stuff to get it working

Categories