How to translate language in Symfony 2.1, according to accept language header? - symfony-2.1

How we can make automatically go to german, if browser Accept language is de_DE or de(German), else go to english(all other browser Accept languages)?
my listener
namespace xxxx
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class LocaleListener
{
private $container;
private $defaultLocale;
public function __construct(ContainerInterface $container, $defaultLocale)
{
$this->container = $container;
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
if (!$this->container->has('request')) {
return;
}
$request = $this->container->get('request');
$request->setLocale($request->getPreferredLanguage($this->defaultLocale));
}
}
service.yml
my_locale_listener:
class: Wishbot\WebBundle\Listener\LocaleListener
tags: [{ name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 100 }]
arguments: [ #service_container, ['de','en' ] ]

I suggest you go through the translation chapter in Symfony 2 documentation.
http://symfony.com/doc/2.0/book/translation.html
It will explain you as to how to maintain your site in different languages.

Related

How to extend Node classes for abstraction purposes within Cypress?

Goal: Create a URL abstraction for our specific product by extending Node's URL class within a Cypress project.
Problem: Cypress executes everything within a browser context, so you can only use node modules in the plugins file which executes within a Node environment. However, I don't really need to execute anything, I just want to extend the available Node objects so I don't have to reimplement these abstractions myself.
I get the following error in Cypress when I set the baseUrl to new ProductURL().toString():
Code:
import { URL } from 'url';
export class ProductURL extends URL {
static readonly DEFAULT_URL: string = "https://example.com";
static readonly DEFAULT_AUTH: BasicAuthCredentials = {
username: Cypress.env('BASIC_AUTH_USERNAME'),
password: Cypress.env('BASIC_AUTH_PASSWORD')
}
private domain: string;
private subdomain: string;
constructor() {
super(ProductURL.DEFAULT_URL);
this.setAuth(ProductURL.DEFAULT_AUTH);
}
public getDomain(): string {
return this.domain;
}
public getSubdomain(): string {
return this.subdomain;
}
public setPort(port: number) {
this.port = String(port);
}
public setProtocol(protocol: Protocol) {
this.protocol = protocol;
}
public setAuth(auth: BasicAuthCredentials | null) {
this.username = auth ? auth.username : '';
this.password = auth ? auth.password : '';
}
}
export enum Protocol {
HTTP = "http",
HTTPS = "https"
}
export type BasicAuthCredentials = {
username: string;
password: string;
};
Question: How can I extend Node classes for abstraction purposes within Cypress context?
Nevermind.
It was creating a circular dependency because the URL class was already loaded, so the import statement:
import { URL } from 'url'
was unnecessary.

SpringBootTest does not load context

I am trying to write functional-test case for a rest controller. According to plan I'd like to start application and using TestRestTemplate call appropriate endpoint. Unfortunately, I am unable to do so because occurring error:
Cannot invoke method exchange() on null object
java.lang.NullPointerException: Cannot invoke method exchange() on null object
...
I can't figure out why and it is the only error message that I get. I feel confused ://
In addition, I'm using Spock framework for testing.
Thanks for your help in advance.
gradle.build
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'org.unbroken-dome.test-sets' version '4.0.0'
id 'java'
id 'groovy'
id 'maven-publish'
}
apply from: "${rootDir}/gradle/test.gradle"
repositories {
mavenCentral()
}
dependencies {
implementation(group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.6.3')
testImplementation(group: 'org.codehaus.groovy', name: 'groovy-all', version: '3.0.9')
testImplementation(group: 'org.spockframework', name: 'spock-core', version: '2.0-groovy-3.0')
testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.6.3')
}
// some more usual Gradle stuff
test.gradle
testSets {
integrationTest { dirName = 'integration-test' }
functionalTest { dirName = 'functional-test' }
}
integrationTest {
mustRunAfter(test)
}
check.dependsOn(integrationTest)
functionalTest {
mustRunAfter(integrationTest)
}
check.dependsOn(functionalTest)
tasks.withType(Test) {
useJUnitPlatform()
}
GreetingControllerSpec
package tchorzyksen
/* imports */
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class GreetingControllerSpec extends Specification {
#Autowired
private TestRestTemplate testRestTemplate
void "get greeting"() {
when:
ResponseEntity<GreetingEntity> response = get("/greeting", GreetingEntity.class)
then:
response.getStatusCode() == HttpStatus.OK
}
protected <T> ResponseEntity<T> get(String uri, Class<T> responseClass) {
return testRestTemplate.exchange(uri, HttpMethod.GET, null, responseClass, [:])
}
}
MyWs.java
package tchorzyksen;
/* imports */
#SpringBootApplication
public class MyWs {
public static void main(String[] args) {
SpringApplication.run(MyWs.class, args);
}
}
and GreetingController
package tchorzyksen.ui.model.controller;
/* imports */
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#GetMapping("/greeting")
public ResponseEntity<GreetingEntity> greeting(
#RequestParam(value = "name", defaultValue = "World") String name) {
return ResponseEntity.ok(
new GreetingEntity(counter.incrementAndGet(), String.format(template, name)));
}
}
Instead
#Autowired
private TestRestTemplate testRestTemplate
Enough
private TestRestTemplate testRestTemplate = new TestRestTemplate();
For all people interested in solution to this question. We are missing one dependency here
testImplementation group: 'org.spockframework', name: 'spock-spring', version: '2.1-M2-groovy-3.0'
Which basically makes it possible to write tests in Spock and use spring test context.
Save yourselfs guys it took me 4 hours to discover (very sad face) ://

Symfony2 custom security voter not working

I implementend my own security voter for my symfony (2.6.1) based project. I did so following this blog entry: http://symfony.com/blog/new-in-symfony-2-6-simpler-security-voters and this documentation: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html
My problem now is, that the method "isGranted" of my voter never gets called.
My voter class looks like this:
namespace AppBundle\SecurityVoter;
use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
use Symfony\Component\Security\Core\User\UserInterface;
class FolderVoter extends AbstractVoter
{
const EDIT = 'edit';
protected function getSupportedClasses()
{
return array('\MyApp\Entity\Folder');
}
protected function getSupportedAttributes()
{
return array(self::EDIT);
}
protected function isGranted($attribute, $folder, $user = null)
{
if (!$user instanceof UserInterface) {
return false;
}
if ($folder->getUserId() == $user->getId()) {
return true;
}
return false;
}
}
The class is configured in the services.yml in the following way:
security.access.folder_voter:
class: AppBundle\SecurityVoter\FolderVoter
public: false
tags:
- { name: security.voter }
I'm using the method 'is_granted' from within a twig template. What did i miss to implement or what did i do wrong that it is not working ?
I just found the solution myself. It was pretty easy. But nasty in some way as well.
Insted of this:
protected function getSupportedClasses()
{
return array('\MyApp\Entity\Folder');
}
I had to declare the class name like this. So without the leading backslash:
protected function getSupportedClasses()
{
return array('MyApp\Entity\Folder');
}
Maybe this could be improved on the security voter implementation in general :-)

Symfony2 extending DefaultAuthenticationSuccessHandler

I want to alter default authentication process just after authentication success. I made a service that is called after authentication success and before redirect.
namespace Pkr\BlogUserBundle\Handler;
use Doctrine\ORM\EntityManager;
use Pkr\BlogUserBundle\Service\Encoder\WpTransitionalEncoder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\Response;
class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
{
protected $entityManager = null;
protected $logger = null;
protected $encoder = null;
public function __construct(EntityManager $entityManager, LoggerInterface $logger, WpTransitionalEncoder $encoder)
{
$this->entityManager = $entityManager;
$this->logger = $logger;
$this->encoder = $encoder;
}
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* #param Request $request
* #param TokenInterface $token
*
* #return Response never null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$newPass = $request->get('_password');
$user->setUserPassword($this->encoder->encodePassword($newPass, null));
$this->entityManager->persist($user);
$this->entityManager->flush();
//do redirect
}
}
in services.yml
services:
pkr_blog_user.wp_transitional_encoder:
class: "%pkr_blog_user.wp_transitional_encoder.class%"
arguments:
cost: "%pkr_blog_user.wp_transitional_encoder.cost%"
logger: #logger
pkr_blog_user.login_success_handler:
class: Pkr\BlogUserBundle\Handler\AuthenticationSuccessHandler
arguments:
entity_manager: #doctrine.orm.entity_manager
logger: #logger
encoder: #pkr_blog_user.wp_transitional_encoder
and in security.yml
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: pkr_blog_admin_login
check_path: pkr_blog_admin_login_check
success_handler: pkr_blog_user.login_success_handler
logout:
path: pkr_blog_admin_logout
target: /
What I'm trying achieve is to just alter default behavior a little so I think why not to extend DefaultAuthenticationSuccessHandler, add something to onSuccessHandler() and call parent::onSucessHandler(). I tried and the problem is that I have no clue how to add security parameters (set in security.yml) to my extended class constructor. DefaultAuthenticationSuccessHandler uses HttpUtils and $options array:
/**
* Constructor.
*
* #param HttpUtils $httpUtils
* #param array $options Options for processing a successful authentication attempt.
*/
public function __construct(HttpUtils $httpUtils, array $options)
{
$this->httpUtils = $httpUtils;
$this->options = array_merge(array(
'always_use_default_target_path' => false,
'default_target_path' => '/',
'login_path' => '/login',
'target_path_parameter' => '_target_path',
'use_referer' => false,
), $options);
}
So my extended class constructor should look like:
// class extends DefaultAuthenticationSuccessHandler
protected $entityManager = null;
protected $logger = null;
protected $encoder = null;
public function __construct(HttpUtils $httpUtils, array $options, EntityManager $entityManager, LoggerInterface $logger, WpTransitionalEncoder $encoder)
{
$this->entityManager = $entityManager;
$this->logger = $logger;
$this->encoder = $encoder;
}
It's quite easy to add HttpUtils service to my services.yml, but what with options argument?
services:
pkr_blog_user.wp_transitional_encoder:
class: "%pkr_blog_user.wp_transitional_encoder.class%"
arguments:
cost: "%pkr_blog_user.wp_transitional_encoder.cost%"
logger: #logger
pkr_blog_user.login_success_handler:
class: Pkr\BlogUserBundle\Handler\AuthenticationSuccessHandler
arguments:
httputils: #security.http_utils
options: [] #WHAT TO ADD HERE ?
entity_manager: #doctrine.orm.entity_manager
logger: #logger
encoder: #pkr_blog_user.wp_transitional_encoder
If you only have one success / failure handler defined for your application, there's a slightly easier way to do this. Rather than define a new service for the success_handler and failure_handler, you can override security.authentication.success_handler and security.authentication.failure_handler instead.
Example:
services.yml
services:
security.authentication.success_handler:
class: StatSidekick\UserBundle\Handler\AuthenticationSuccessHandler
arguments: ["#security.http_utils", {}]
tags:
- { name: 'monolog.logger', channel: 'security' }
security.authentication.failure_handler:
class: StatSidekick\UserBundle\Handler\AuthenticationFailureHandler
arguments: ["#http_kernel", "#security.http_utils", {}, "#logger"]
tags:
- { name: 'monolog.logger', channel: 'security' }
AuthenticationSuccessHandler.php
<?php
namespace StatSidekick\UserBundle\Handler;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
use Symfony\Component\Security\Http\HttpUtils;
class AuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler {
public function __construct( HttpUtils $httpUtils, array $options ) {
parent::__construct( $httpUtils, $options );
}
public function onAuthenticationSuccess( Request $request, TokenInterface $token ) {
if( $request->isXmlHttpRequest() ) {
$response = new JsonResponse( array( 'success' => true, 'username' => $token->getUsername() ) );
} else {
$response = parent::onAuthenticationSuccess( $request, $token );
}
return $response;
}
}
AuthenticationFailureHandler.php
<?php
namespace StatSidekick\UserBundle\Handler;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
use Symfony\Component\Security\Http\HttpUtils;
class AuthenticationFailureHandler extends DefaultAuthenticationFailureHandler {
public function __construct( HttpKernelInterface $httpKernel, HttpUtils $httpUtils, array $options, LoggerInterface $logger = null ) {
parent::__construct( $httpKernel, $httpUtils, $options, $logger );
}
public function onAuthenticationFailure( Request $request, AuthenticationException $exception ) {
if( $request->isXmlHttpRequest() ) {
$response = new JsonResponse( array( 'success' => false, 'message' => $exception->getMessage() ) );
} else {
$response = parent::onAuthenticationFailure( $request, $exception );
}
return $response;
}
}
In my case, I was just trying to set something up so that I could get a JSON response when I try to authenticate using AJAX, but the principle is the same.
The benefit of this approach is that without any additional work, all of the options that are normally passed into the default handlers should get injected correctly. This happens because of how SecurityBundle\DependencyInjection\Security\Factory is setup in the framework:
protected function createAuthenticationSuccessHandler($container, $id, $config)
{
...
$successHandler = $container->setDefinition($successHandlerId, new DefinitionDecorator('security.authentication.success_handler'));
$successHandler->replaceArgument(1, array_intersect_key($config, $this->defaultSuccessHandlerOptions));
...
}
protected function createAuthenticationFailureHandler($container, $id, $config)
{
...
$failureHandler = $container->setDefinition($id, new DefinitionDecorator('security.authentication.failure_handler'));
$failureHandler->replaceArgument(2, array_intersect_key($config, $this->defaultFailureHandlerOptions));
...
}
It specifically looks for security.authentication.success_handler and security.authentication.failure_handler in order to merge options from your config into the arrays passed in. I'm sure there's a way to setup something similar for your own service, but I haven't looked into it yet.
Hope that helps.
You can easily see how default security listeners are manage in this file :
vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml
For example, DefaultAuthenticationSuccessHandler is registered like that:
<!-- Parameter -->
<parameter key="security.authentication.success_handler.class">Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler</parameter>
<!-- Service -->
<service id="security.authentication.success_handler" class="%security.authentication.success_handler.class%" abstract="true" public="false">
<argument type="service" id="security.http_utils" />
<argument type="collection" /> <!-- Options -->
</service>
So finally we can see that the option collection is empty by default !
options: {} will do the job ^^ (Think a collection is represent by {} in yaml)
For the best solution so far scroll to bottom of this answer
OK I finally got it working in a way I wanted. The problem was that Symfony2 was not passing config array from security.yml to constructor when custom handler is set. So what I did was:
1) I removed custom handler declaration from security.yml
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: pkr_blog_admin_login
check_path: pkr_blog_admin_login_check
logout:
path: pkr_blog_admin_logout
target: /
2) AuthenticationSuccessHandler extends default handler class, rehash user password and finally let default handler do the rest. Two new arguments was added in constructor:
#/src/Pkr/BlogUserBundle/Handler/AuthenticationSuccessHandler.php
namespace Pkr\BlogUserBundle\Handler;
use Doctrine\ORM\EntityManager;
use Pkr\BlogUserBundle\Service\Encoder\WpTransitionalEncoder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
use Symfony\Component\Security\Http\Authentication\Response;
use Symfony\Component\Security\Http\HttpUtils;
class AuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler
{
protected $entityManager = null;
protected $logger = null;
protected $encoder = null;
public function __construct(
HttpUtils $httpUtils,
array $options,
// new arguments below
EntityManager $entityManager = null, # entity manager
WpTransitionalEncoder $encoder = null
)
{
$this->entityManager = $entityManager;
$this->encoder = $encoder;
parent::__construct($httpUtils, $options);
}
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* #param Request $request
* #param TokenInterface $token
*
* #return Response never null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
if (preg_match('^\$P\$', $user->getUserPassword())) {
$newPass = $request->get('_password');
$user->setUserPassword($this->encoder->encodePassword($newPass, null));
$this->entityManager->persist($user);
$this->entityManager->flush();
}
return parent::onAuthenticationSuccess($request, $token);
}
}
3) added and changed some parameters in my services.yml so I could use them in my compiler pass class:
#/src/Pkr/BlogUserBundle/Resources/config/services.yml
parameters:
pkr_blog_user.wp_transitional_encoder.cost: 20
# password encoder class
pkr_blog_user.wp_transitional_encoder.class: Pkr\BlogUserBundle\Service\Encoder\WpTransitionalEncoder
# authentication success handler class
pkr_blog_user.login_success_handler.class: Pkr\BlogUserBundle\Handler\AuthenticationSuccessHandler
# entity manager service name
pkr_blog_user.login_success_handler.arg.entity_manager: doctrine.orm.entity_manager
# encoder service name
pkr_blog_user.login_success_handler.arg.encoder: pkr_blog_user.wp_transitional_encoder
services:
pkr_blog_user.wp_transitional_encoder:
class: "%pkr_blog_user.wp_transitional_encoder.class%"
arguments:
cost: "%pkr_blog_user.wp_transitional_encoder.cost%"
logger: #logger
pkr_blog_user.login_success_handler:
class: "%pkr_blog_user.login_success_handler.class%"
4) created a compiler pass class RehashPasswordPass that changes default authentication success handler and adds some parameters to constructor:
#/src/Pkr/BlogUserBundle/DependencyInjection/Compiler/RehashPasswordPass.php
namespace Pkr\BlogUserBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class RehashPasswordPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition('security.authentication.success_handler')) {
// definition of default success handler
$def = $container->getDefinition('security.authentication.success_handler');
// changing default class
$def->setClass($container->getParameter('pkr_blog_user.login_success_handler.class'));
$entityMngRef = new Reference(
$container->getParameter("pkr_blog_user.login_success_handler.arg.entity_manager")
);
// adding entity manager as third param to constructor
$def->addArgument($entityMngRef);
$encoderRef = new Reference(
$container->getParameter("pkr_blog_user.login_success_handler.arg.encoder")
);
// adding encoder as fourth param to constructor
$def->addArgument($encoderRef);
}
}
}
5) added compiler pass to container builder:
#/src/Pkr/BlogUserBundle/PkrBlogUserBundle.php
namespace Pkr\BlogUserBundle;
use Pkr\BlogUserBundle\DependencyInjection\Compiler\RehashPasswordPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class PkrBlogUserBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new RehashPasswordPass());
}
}
Now default handler class was changed but symfony will still pass configuration from security.yml to constructor plus two new arguments added by compiler pass.
The better way
Event handler as a service with setters
#/src/Pkr/BlogUserBundle/Resources/config/services.yml
parameters:
pkr_blog_user.wp_transitional_encoder.cost: 15
# password encoder class
pkr_blog_user.wp_transitional_encoder.class: Pkr\BlogUserBundle\Service\Encoder\WpTransitionalEncoder
# authentication success handler class
pkr_blog_user.authentication_success_handler.class: Pkr\BlogUserBundle\EventHandler\AuthenticationSuccessHandler
services:
pkr_blog_user.wp_transitional_encoder:
class: "%pkr_blog_user.wp_transitional_encoder.class%"
arguments:
cost: "%pkr_blog_user.wp_transitional_encoder.cost%"
logger: #logger
pkr_blog_user.authentication_success_handler:
class: "%pkr_blog_user.authentication_success_handler.class%"
calls:
- [ setRequest, [ #request ]]
- [ setEntityManager, [ #doctrine.orm.entity_manager ]]
- [ setEncoder, [ #pkr_blog_user.wp_transitional_encoder ]]
tags:
- { name: kernel.event_listener, event: security.authentication.success , method: handleAuthenticationSuccess }
Event handler class
# /src/Pkr/BlogUserBundle/EventHandler/AuthenticationSuccessHandler.php
namespace Pkr\BlogUserBundle\EventHandler;
use Doctrine\ORM\EntityManager;
use Pkr\BlogUserBundle\Service\Encoder\WpTransitionalEncoder;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
class AuthenticationSuccessHandler {
protected $entityManager = null;
protected $encoder = null;
public function setRequest(Request $request)
{
$this->request = $request;
}
public function setEntityManager(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function setEncoder(WpTransitionalEncoder $encoder)
{
$this->encoder = $encoder;
}
public function handleAuthenticationSuccess(AuthenticationEvent $event)
{
$token = $event->getAuthenticationToken();
$user = $token->getUser();
if (preg_match('^\$P\$', $user->getUserPassword())) {
$newPass = $this->request->get('_password');
$user->setUserPassword($this->encoder->encodePassword($newPass, null));
$this->entityManager->persist($user);
$this->entityManager->flush();
}
}
}
And it's all working, no compiler pass needed. Why didn't I thought of that from the begining...
Uhh it stopped working after symfony update
Now I get exception:
ScopeWideningInjectionException: Scope Widening Injection detected: The definition "pkr_blog_user.authentication_success_handler" references the service "request" which belongs to a narrower scope. Generally, it is safer to either move "pkr_blog_user.authentication_success_handler" to scope "request" or alternatively rely on the provider pattern by injecting the container itself, and requesting the service "request" each time it is needed. In rare, special cases however that might not be necessary, then you can set the reference to strict=false to get rid of this error.
It seems that I need to pass full container to my service. So I modified services.yml and event handler class.
#/src/Pkr/BlogUserBundle/Resources/config/services.yml
parameters:
pkr_blog_user.wp_transitional_encoder.cost: 15
# password encoder class
pkr_blog_user.wp_transitional_encoder.class: Pkr\BlogUserBundle\Service\Encoder\WpTransitionalEncoder
# authentication success handler class
pkr_blog_user.authentication_success_handler.class: Pkr\BlogUserBundle\EventHandler\AuthenticationSuccessHandler
services:
pkr_blog_user.wp_transitional_encoder:
class: "%pkr_blog_user.wp_transitional_encoder.class%"
arguments:
secure: #security.secure_random
cost: "%pkr_blog_user.wp_transitional_encoder.cost%"
pkr_blog_user.authentication_success_handler:
class: "%pkr_blog_user.authentication_success_handler.class%"
arguments:
container: #service_container
tags:
- { name: kernel.event_listener, event: security.authentication.success , method: handleAuthenticationSuccess }
And event handler
# /src/Pkr/BlogUserBundle/EventHandler/AuthenticationSuccessHandler.php
namespace Pkr\BlogUserBundle\EventHandler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
class AuthenticationSuccessHandler
{
/**
* #var ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function handleAuthenticationSuccess(AuthenticationEvent $event)
{
$request = $this->container->get('request');
$em = $this->container->get('doctrine.orm.entity_manager');
$encoder = $this->container->get('pkr_blog_user.wp_transitional_encoder');
$token = $event->getAuthenticationToken();
$user = $token->getUser();
if (preg_match('/^\$P\$/', $user->getUserPassword())) {
$newPass = $request->get('_password');
$user->setUserPassword($encoder->encodePassword($newPass, null));
$em->persist($user);
$em->flush();
}
}
}
And it works again.
Best way so far
The solution above was best I knew until #dmccabe wrote his solution.
Unfortunately by using the success_handler option in the security configuration you can't provide a custom listener that extends DefaultAuthenticationSuccessHandler.
Not until this issue is fixed: Symfony issue - [2.1][Security] Custom AuthenticationSuccessHandler
Until then the simplest solution is what #dmccabe suggested:
Globaly overwrite the security.authentication.success_handler which is fine as long as you don't need to have multiple handlers for multiple firewalls.
If you do (as of this writing-) you have to write your own Authentication Provider.
actually the best way to do this is to extend default auth handler as service
authentication_handler:
class: AppBundle\Service\AuthenticationHandler
calls: [['setDoctrine', ['#doctrine']]]
parent: security.authentication.success_handler
public: false
and the AuthenticationHandler class would look like
class AuthenticationHandler extends DefaultAuthenticationSuccessHandler
{
/**
* #var Registry
*/
private $doctrine;
public function setDoctrine(Registry $doctrine)
{
$this->doctrine = $doctrine;
}
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* #param Request $request
* #param TokenInterface $token
*
* #return Response never null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
// do whatever you like here
// ...
// call default success behaviour
return parent::onAuthenticationSuccess($request, $token);
}
}

symfony2 detect locale based on domain name

i want to setup a new website with 2 locales and the locale should detected by the domain name that's used. Any idea how to do this?
for example locales: nl and fr
when www.somenldomainname.be is used then the nl locale should be detected
when www.somefrdomainname.be is used then the fr locale should be detected
it would also be great if i generate an url in nl or fr the right domain name is selected.
kind regards,
Daan
You can create an event listener to detect your domain name:
class LocaleListener implements EventSubscriberInterface
{
/**
* Set default locale
*
* #param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// get domain name
$host = $request->getHttpHost();
// or $host = $request->getHost();
$locale = 'en';
if ($host == 'domain1') {
$locale = 'fr';
}
$request->setLocale($locale);
}
/**
* {#inheritdoc}
*/
static public function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest', 17)),
);
}
}
And in your services.yml:
services:
my_locale_listener:
class: MyBundle\LocaleListener
tags:
- { name: kernel.event_subscriber }
You can put in listener constructor default locle from parameters.yml file and if locale is not detected by domain set default locale.
There is a bundle for that: https://github.com/schmittjoh/JMSI18nRoutingBundle.
This is how you set it up in config.yml:
jms_i18n_routing:
default_locale: nl
locales: [nl, fr]
strategy: custom
hosts:
nl: www.somenldomainname.be
fr: www.somefrdomainname.be
redirect_to_host: true
See documentation on usage scenarios for more details.
The bundle JMSI18nRoutingBundle only supports symfony <=2.1.x.
The good way seems to use the solution of Daniel Korsak. Here is a more complete exemple with parameters.
namespace Path\ToYourBundle\Listeners;
use \Symfony\Component\HttpKernel\Event\GetResponseEvent;
use \Symfony\Component\HttpKernel\KernelEvents;
use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
class LocaleListener implements EventSubscriberInterface
{
protected $domainLocales;
protected $defaultLocale;
public function __construct($container,$defaultLocale)
{
$this->domainLocales = $container->getParameter('domain_locales');
$this->defaultLocale = $defaultLocale;
}
/**
* Set default locale
*
* #param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// get domain name
$host = $request->getHttpHost();
// or $host = $request->getHost();
$locale = $this->defaultLocale;
if (array_key_exists($host, $this->domainLocales))
{
$locale = $this->domainLocales[$host];
}
$request->setLocale($locale);
}
/**
* {#inheritdoc}
*/
static public function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest', 17)),
);
}
}
And in your services.yml:
services:
my_locale_listener:
class: Path\ToYourBundle\Listeners\LocaleListener
tags:
- { name: kernel.event_subscriber }
arguments: [#service_container,%locale%]
parameters:
domain_locales:
domain1: en
domain2: fr

Resources