Authentication event not being called - symfony

I have the following simple class:
<?php
namespace App\EventListener;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
class LoginListener implements EventSubscriberInterface
{
private $logger = null;
public function __construct (LoggerInterface $logger)
{
$this->logger = $logger;
}
public function onInteractiveLogin (InteractiveLoginEvent $event)
{
$this->logger->info('TEST');
}
public static function getSubscribedEvents ()
{
return [
SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin',
];
}
}
It is under the /src and should autoload (Symfony 4.1.0).
When I run:
bin/console debug:event-dispatcher security.interactive_login
I see expected results:
Registered Listeners for "security.interactive_login" Event
------- ------------------------------------------------------- ----------
Order Callable Priority
------- ------------------------------------------------------- ----------
#1 App\EventListener\LoginListener::onInteractiveLogin() 0
------- ------------------------------------------------------- ----------
And yet when I login successfully (or otherwise) that log message isn't where there - digging into Symfony profiler - I can see it's under the "Not Called Listeners" tab???
Any ideas what I am missing?
EDIT |
services.yaml:
imports:
- { resource: 'company.yaml' }
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
public: false # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{Entity,Migrations,Tests,Legacy}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
#
# ldap authentication service configuration
# https://symfony.com/doc/current/reference/configuration/security.html
Symfony\Component\Ldap\Ldap:
arguments: ['#Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: davinci-1
port: 389
#encryption: tls
options:
protocol_version: 3
referrals: false
#
# We need to hook into default LDAP authentication to properly populate Symfony
# roles and / or return legacy groups
# http://symfony.com/doc/current/components/security/authentication.html#authentication-events
# http://symfony.com/doc/current/event_dispatcher.html
#App\EventListener\LoginListener:
# tags:
# - { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin }
security.yaml
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
ad_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: dc=company,dc=local
search_dn: 'appuser'
search_password: "XXX"
default_roles: ROLE_USER
uid_key: 'sAMAccountName'
filter: '({uid_key}={username})'
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
http_basic_ldap:
provider: ad_ldap
service: Symfony\Component\Ldap\Ldap
dn_string: 'Company\{username}'
#query_string: '(sAMAccountName={username})'
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }

Related

Error service symfony : A namer must be configured

I want to use create folder function with vich considering id.
My service:
<?php
namespace App\Service\Namers;
use Vich\UploaderBundle\Mapping\PropertyMapping;
use Vich\UploaderBundle\Naming\DirectoryNamerInterface;
use App\Entity\Users;
class VichNamer implements DirectoryNamerInterface {
public function directoryName(object $object, PropertyMapping $mapping): string {
return 'test';
}
}
my vich_uploader.yaml:
vich_uploader:
db_driver: orm
metadata:
type: attribute
mappings:
tattoo_images:
uri_prefix: /images/
upload_destination: '%kernel.project_dir%/public/images/'
directory_namer: App\Service\Namers\VichNamer
My services.yaml:
parameters:
images_directory: '%kernel.project_dir%/public/uploads'
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
App\EventSubscriber\EasyAdminSubscriber:
tags:
- { name: 'doctrine.event_subscriber', event: preUpdate }
App\Service\Namers\VichNamer:
public: true
tags:
- { name: 'vich.namedirectory' }
My error:
A namer must be configured.
I user symfoy 6.2
thank you
The namer is used to name the files and directories it saves to the filesystem
You can add nammer by default like this in your config:
vich_uploader:
.....
mappings:
tattoo_images:
uri_prefix: /images/
upload_destination: '%kernel.project_dir%/public/images/'
directory_namer: App\Service\Namers\VichNamer
namer:
service: Vich\UploaderBundle\Naming\PropertyNamer
options: { property: 'slug' }
You can see all options working with namer here : namers

Problem with setting entity manager other than default in user provider for Symfony 5

I looked in to this post but my problem is a little different and many things in symfony security component have changed for version 5.
So, I try to set entity manager other than default in user provider. I created two connections using documentation: https://symfony.com/doc/current/doctrine/multiple_entity_managers.html
doctrine:
dbal:
default_connection: default
connections:
default:
url: '%env(resolve:DATABASE_URL)%'
b2b:
url: '%env(resolve:DATABASE_B2B_URL)%'
users:
url: '%env(resolve:DATABASE_USERS_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '5.7'
orm:
auto_generate_proxy_classes: true
default_entity_manager: default
entity_managers:
default:
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: false
connection: default
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Main'
prefix: 'App\Entity\Main'
alias: Main
b2b:
connection: b2b
users:
connection: users
mappings:
Users:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Users'
prefix: 'App\Entity\Users'
alias: Users
Next I change my security.yaml and add manager_name: users - exactly as in the documentation:
security:
encoders:
App\Entity\Users\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\Users\User
property: email
manager_name: users
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: app_user_provider
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
Despite the above settings security component try to load class from default entity manager:
[2020-04-16T05:53:55.276798+00:00] request.CRITICAL: Uncaught PHP Exception Doctrine\Persistence\Mapping\MappingException: "The class 'App\Entity\Users\User' was not found in the chain configured namespaces App\Entity\Main" at /home/budmechzz/public_html/rfm.computermedia.com.pl/vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/MappingException.php line 23 {"exception":"[object] (Doctrine\\Persistence\\Mapping\\MappingException(code: 0): The class 'App\\Entity\\Users\\User' was not found in the chain configured namespaces App\\Entity\\Main at /home/budmechzz/public_html/rfm.computermedia.com.pl/vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/MappingException.php:23)"} []
What am I doing wrong?
Ok, I think I solved the problem.
The reason was the class /src/Security/LoginFormAuthenticator.php (automatically generated by php bin / console make: auth) in 68 line is:
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
This line is trying to get the user from the default entity manager. I do not know why manager indication doesn't help eg. $this->entityManager->getRepository(User::class, 'users')
So I change __constructor in /src/Security/LoginFormAuthenticator.php and I get entity manager from ManagerRegistry (It was previously geted from EntityManagerInterface). This solves my login problem :)
use Doctrine\Common\Persistence\ManagerRegistry;
public function __construct(ManagerRegistry $managerRegistry, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $managerRegistry->getManager('users');
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
I hope it will be useful to someone

Could not resolve argument $encoder of "xx", UserPasswordEncoderInterface, Symfony 4.*

I'm trying to encode the password im passing through post but it is giving me an error when I declare the argument in the function.
Could not resolve argument $encoder of "App\Controller\UsuarioController::altapropietario()", maybe you forgot to register the controller as a service or missed tagging it with the "controller.service_arguments"?*
Here I paste the whole function, it has a form and its just for registering new users.
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UsuarioController extends AbstractController
{
public function altaPropietario(Request $request, UserPasswordEncoderInterface $encoder )
{
$propietario = new Usuario();
$form = $this->createForm(AltaPropietarioType::class, $propietario);
$form->handleRequest($request);
if ($form->isSubmitted())
{
$propietario->setRole("ROLE_PROP");
$encoded = $encoder->encodePassword($propietario, $propietario->getPassword());
$propietario->setPassword($encoded);
// rest of the implementation not included.
}
}
}
My security yml is the following:
security:
encoders:
App\Entity\Usuario: bcrypt
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
users_in_memory: { memory: null }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: users_in_memory
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
services.yaml:
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

How to extend LdapUserProvider and use a custom LDAP user provider in Symfony?

I'm struggeling to replace the LdapUserProvider.
I created my own provider (App\Security\MyLdapUserProvider based on LdapUserProvider but retrieves more information) and my own UserInterface (App\Security\MyUser) with more attributes to store the data.
In the end I want to retrieve the groups and the displayName of the user.
Here is my config:
services.yaml:
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
Symfony\Component\Ldap\Ldap:
arguments: ['#Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: 10.106.1.1
port: 389
#encryption: tls
options:
protocol_version: 3
referrals: false
security.yaml:
providers:
#in_memory: { memory: ~ }
my_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: "dc=XXXXXX,dc=com"
search_dn: "CN=XXXXXXXXXX,OU=LDAP,OU=Services Accounts,OU=Administration,DC=XXXXXXXXX,DC=com"
search_password: "ergergergergerg"
default_roles: ROLE_USER
filter: "({uid_key}={username})"
uid_key: samAccountName
#password_attribute: displayName
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
security: true
anonymous: true
provider: my_ldap
form_login_ldap:
login_path: /login
check_path: /login
service: Symfony\Component\Ldap\Ldap
dn_string: 'dc=XXXXXX,dc=com'
query_string: '(samAccountName={username})'
logout:
path: /logout
target: /
Where can I tell the security provider to use my own ldap provider instead of the default one ?
Symfony processes are still a bit complicated to me so if someone can take time to explain..
Symfony docs is an endless loop of redirecting between CustomUserProvider > Ldap config > CustomeUSerProvider...
As described in the documentation chapter Creating A Custom User Provider you need to add your User Provider as a new key under security.providers and configure it's id.
This id is the name of of your custom User Provider service which - in recent versions of symfony - equals the FQCN .
# security.yaml
security:
providers:
# the name of your user provider can be anything
my_ldap_provider:
id: 'App\Security\MyLdapUserProvider'
Then you can use this provider for one of the firewalls like this:
security:
# [..]
firewalls:
main:
pattern: '^/'
provider: 'my_ldap_provider'
Symfony's LdapUserProvider looks like this:
class LdapUserProvider implements UserProviderInterface
{
private $ldap;
private $baseDn;
private $searchDn;
private $searchPassword;
private $defaultRoles;
private $uidKey;
private $defaultSearch;
private $passwordAttribute;
private $extraFields;
public function __construct(
LdapInterface $ldap,
string $baseDn,
string $searchDn = null,
string $searchPassword = null,
array $defaultRoles = [],
string $uidKey = null,
string $filter = null,
string $passwordAttribute = null,
array $extraFields = []
)
{
In order to create your MyLdapUserProvider service that extends LdapUserProvider correctly you need a service-definition like this:
# services.yaml
services:
App\Security\MyLdapUserProvider:
arguments:
$adminEmail: '%admin_email%'
$ldap: '#Symfony\Component\Ldap\Ldap'
$baseDn: 'dc=XXXXXX,dc=com'
$searchDn: 'CN=XXXXXXXXXX,OU=LDAP,OU=Services Accounts,OU=Administration,DC=XXXXXXXXX,DC=com'
$searchPassword: 'ergergergergerg'
$defaultRoles: ['ROLE_USER']
$filter: '({uid_key}={username})'
$uidKey: 'samAccountName'

Custom FOSUBUserProvider not working properly

I've been following the instruction here: https://gist.github.com/danvbe/4476697, I have read the entire thread more than once, but I'm not getting a solution for my problem.
I want to use the oauth bundle just for account linking, persisting the user data from oauth provider. My users will not be authenticated using oauth.
Nevertheless, I have implemented the whole thing to see if it works with github as provider, but nothing. I'm able to go to the authorization page, but when I click on Allow Access, I'm inevitable redirected to the login page with this error No oauth code in the request.
If stop using the custom FOSUBUserProvider and change to the default HWI one, then I get the app registered in Github but cannot persist the data.
Important: I tried replicating exactly the FOSUBUserProvider from HWI and the same problem remained, so probably is not related it's implementation but maybe with the service definition or the config.
Any help is greatly appreciated.
These are the relevant files:
FOSUBUserProvider.php
class FOSUBUserProvider extends BaseClass
{
/**
* {#inheritDoc}
*/
public function connect(UserInterface $user, UserResponseInterface $response)
{
$property = $this->getProperty($response);
$username = $response->getUsername();
//on connect - get the access token and the user ID
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
//we "disconnect" previously connected users
if (null !== $previousUser = $this->userManager->findUserBy(array($property => $username))) {
$previousUser->$setter_id(null);
$previousUser->$setter_token(null);
$this->userManager->updateUser($previousUser);
}
//we connect current user
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
$this->userManager->updateUser($user);
}
/**
* {#inheritdoc}
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$username = $response->getUsername();
$user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));
//when the user is registrating
if (null === $user) {
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
// create new user here
$user = $this->userManager->createUser();
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
//I have set all requested data with the user's username
//modify here with relevant data
$user->setUsername($username);
$user->setEmail($username);
$user->setPassword($username);
$user->setEnabled(true);
$this->userManager->updateUser($user);
return $user;
}
//if user exists - go with the HWIOAuth way
$user = parent::loadUserByOAuthUserResponse($response);
$serviceName = $response->getResourceOwner()->getName();
$setter = 'set' . ucfirst($serviceName) . 'AccessToken';
//update access token
$user->$setter($response->getAccessToken());
return $user;
}
}
config.yml
hwi_oauth:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on your site, with data from the provider (facebook. google, etc.)
#and also, the connecting part (get the token and the user_id)
connect:
account_connector: custom.user.provider
# name of the firewall in which this bundle is active, this setting MUST be set
firewall_name: main
# optional FOSUserBundle integration
fosub:
# try 30 times to check if a username is available (foo, foo1, foo2 etc)
username_iterations: 30
# mapping between resource owners (see below) and properties
properties:
github: githubId
# optional HTTP Client configuration
http_client:
verify_peer: false
resource_owners:
github:
type: github
client_id: xxxxxxxxxxxxxxxxxxxxxx
client_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
scope: "repo, delete_repo, notifications, gist"
options:
csrf: true
security.yml
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls: #CAUTION! The order of the firewalls IS ON PURPOSE! DON'T CHANGE!
# Disabling the security for the web debug toolbar, the profiler and Assetic.
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
# -> custom firewall for the admin area of the URL
admin:
pattern: /admin(.*)
context: user
form_login:
provider: fos_userbundle
login_path: /admin/login
use_forward: false
check_path: /admin/login_check
failure_path: null
logout:
path: /admin/logout
anonymous: true
# -> end custom configuration
# defaut login area for standard users
# This firewall is used to handle the public login area
# This part is handled by the FOS User Bundle
main:
pattern: .*
context: user
form_login:
provider: fos_userbundle
login_path: /login
use_forward: false
check_path: /login_check
failure_path: null
logout: true
anonymous: true
# Login path for OAuth providers
oauth:
resource_owners:
github: "/login/check-github"
trello: "/login/check-trello"
login_path: /login
failure_path: /login
# FOSUB integration
# oauth_user_provider:
# service: hwi_oauth.user.provider.fosub_bridge
oauth_user_provider:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on website, with data from the provider (github. trello, etc.)
service: custom.user.provider
access_control:
# URL of FOSUserBundle which need to be available to anonymous users
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
# Admin login page needs to be access without credential
- { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
# Secured part of the site
# This config requires being logged for the whole site and having the admin role for the admin part.
# Change these rules to adapt them to your needs
- { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
- { path: ^/.*, role: ROLE_USER } #This is on purpose.
routing.yml
hwi_oauth_security:
resource: "#HWIOAuthBundle/Resources/config/routing/login.xml"
prefix: /connect
hwi_oauth_connect:
resource: "#HWIOAuthBundle/Resources/config/routing/connect.xml"
prefix: /connect
hwi_oauth_redirect:
resource: "#HWIOAuthBundle/Resources/config/routing/redirect.xml"
prefix: /connect
services.yml
parameters:
custom.user.provider.class: My\Bundle\Path\Security\Core\User\FOSUBUserProvider
services:
sonata.admin.user:
class: My\Bundle\Path\Admin\Model\UserAdmin
tags:
# - { name: sonata.admin, manager_type: orm, group: users, label: users, label_translator_strategy: sonata.admin.label.strategy.underscore }
arguments:
- ~
- My\Bundle\Path\Entity\User
- SonataAdminBundle:CRUD
calls:
- [setTranslationDomain, [SonataUserBundle]]
- [setUserManager, [#fos_user.user_manager]]
- [setSecurityContext, [#security.context]]
custom.user.provider:
class: "%custom.user.provider.class%"
#this is the place where the properties are passed to the UserProvider - see config.yml
arguments: [#fos_user.user_manager,{github: github_id, trello: trello_id}]
Well, after a lot of try and error, I found the problem:
The callback URL in Github was: http://mywebsite/login/check-github but that was wrong. The truth is that I never found what this value had to be set up to, so I was guessing. By accident I discovered the right URL: http://mywebsite/connect/service/github applicable in my case, with my configuration.
I found it in one of the times in wich I tried the default HWI Provider, inspecting the redirects with the browser console.

Resources