I am implementing a multi tenant application where the customer requires me to follow a specific data model. The customer required two seperate login systems that I am implementing using the guard system.
The first one is used to login customers. The difficulty here is that it is required to store the users passwords in the customer's database. To accomplish this I have implementation the guard system to first looks up the username in the 'login' database. From that same database the (encrypted) connection parameters for the users application database are retrieved. Using these parameters the users application database connection is created dynamically ('app') and the user is logged in using the information in this application database. On subsequent page requests the users application database is dynamically connected using a service on triggers on each pageload. So far so good.
The second Guard system is used to login to the back-end for management tasks. This login system is actually much simpler and uses a single database 'admin_login' to verify the users credentials. (So username, password and all other user properties are in one single database). The problem occurs after successfully logging in on the 2nd Guard implementation. The forward to the back-end home page triggers an error and I am unable to find to cause of this.
The error message is:
The class 'AppBundle\Entity\Login\Admin\AdminUser' was not found in the chain configured namespaces AppBundle\Entity\App
The error triggers before any code from the corresponding controller is hit. When I set the 'intercept_redirects' configuration item to true I can see that the administrative user is fully authorized before the redirect is taking place.
My doctrine configuration in config.yml
# Doctrine Configuration
doctrine:
dbal:
default_connection: app # specify the connexion used by default
connections:
login:
driver: pdo_mysql
host: '%database_login_host%'
port: '%database_login_port%'
dbname: '%database_login_name%'
user: '%database_login_user%'
password: '%database_login_password%'
charset: utf8mb4
admin_login:
driver: pdo_mysql
host: '%database_admin_host%'
port: '%database_admin_port%'
dbname: '%database_admin_name%'
user: '%database_admin_user%'
password: '%database_admin_password%'
charset: utf8mb4
app:
driver: pdo_mysql
host: '%database_app_host%'
port: '%database_app_port%'
dbname: '%database_app_name%'
user: '%database_app_user%'
password: '%database_app_password%'
charset: utf8mb4
orm:
auto_generate_proxy_classes: "%kernel.debug%"
default_entity_manager: app # specify the EM used by default (when using console commands f.e)
entity_managers:
login:
connection: login
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: false
mappings:
AppBundle :
type: annotation
dir: Entity/Login/Customer
prefix: AppBundle\Entity\Login\Customer
alias: Login
app:
connection: app
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: false
mappings:
AppBundle :
type: annotation
dir: Entity/App
prefix: AppBundle\Entity\App
alias: App
filters:
customer_flt:
class: AppBundle\Security\CustomerFilter
admin:
connection: admin_login
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: false
mappings:
AppBundle :
type: annotation
dir: Entity/Login/Admin
prefix: AppBundle\Entity\Login\Admin
alias: Admin
My security.yml
security:
encoders:
AppBundle\Entity\App\User:
algorithm: bcrypt
cost: 12
AppBundle\Entity\Login\Admin\AdminUser:
algorithm: bcrypt
cost: 12
role_hierarchy:
ROLE_SUPER_ADMIN: ROLE_ADMIN
ROLE_ADMIN: ROLE_USER
# http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
app:
entity:
class: AppBundle\Entity\App\User
property: username
admin:
entity:
class: AppBundle\Entity\Login\Admin\AdminUser
property: login
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/app
anonymous: ~
guard:
authenticators:
- app.security.login_form_authenticator
logout:
path: /app/logout
target: /
provider: app
admin:
pattern: ^/admin
anonymous: ~
guard:
authenticators:
- backend.security.login_form_authenticator
logout:
path: /admin/logout
target: /
provider: admin
access_control:
- { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: ROLE_SUPER_ADMIN }
- { path: ^/app/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/app, roles: ROLE_USER }
What am I missing here?
Issue solved! Just an update so that everyone can find the answer when googling for it.
The issue was caused by a missing manager_name property in the providers section of the security.yml file. This property provides a mapping from a provider to a specific entity_manager. Without these Symfony takes the first (or default) entity manager and that fails (obviously) in my scenario.
providers:
app:
entity:
class: AppBundle\Entity\App\User
property: username
manager_name: app
admin:
entity:
class: AppBundle\Entity\Login\Admin\AdminUser
property: login
manager_name: admin
Sparse documentation of this can be found here: http://symfony.com/doc/current/security/entity_provider.html