In my Symfony project, I configure the security of my application in the security.yaml file, where I define different providers for authentication. In parallel, I have also configured multiple database connections in the doctrine.yaml file to access different databases. My goal is that when a user logs in, they can choose a domain that gives them access to one of three different databases, depending on their choice. Although the databases have the same structure, they do not contain the same data, hence the configuration you can see in the attached files. This configuration is not a choice but a necessity.
To manage the connection to these three databases, I rely on three different routes. Depending on the selected domain, a different route is used. These routes (defined in routes.yaml) are linked to my firewalls in security.yaml, and I link my firewalls to providers to finally associate them with my connections in doctrine.yaml, using the “manager_name” option in the providers. Normally, this option should correspond to the name of my connection in doctrine.yaml.
My routes.yaml :
controllers:
resource: ../src/Controller/
type: attribute
app:
path: /
controller: AppControllerDefaultController::index
requirements:
_method: GET
options:
index: true
fallback:
path: /{url}
controller: AppControllerDefaultController::index
requirements:
url: ".+"
options:
index: true
delete_livrable:
path: /api/deleteLivrable/{id}
controller: AppControllerLivrableController::deleteLivrable
methods: DELETE
api_login_check:
path: /api/login_check
api_login_check_data_processing:
path: /api/login_check_data_processing
api_login_check_data_intelligence:
path: /api/login_check_data_intelligence```
My doctrine.yaml :
`
doctrine:
dbal:
default_connection: data_metier
connections:
data_metier:
dbname: matrics
host: localhost
port: 3306
url: '%env(resolve:DATABASE_URL)%'
driver: "pdo_mysql"
driver_class: AppDBALMyDatabaseDriver
charset: utf8mb4
server_version: '15'
data_processing:
dbname:
host: localhost
port: 3306
url: '%env(resolve:DATABASE_DATA_PROCESSING_URL)%'
driver: "pdo_mysql"
driver_class: AppDBALMyDatabaseDriver
charset: utf8mb4
server_version: '15'
data_intelligence:
dbname: matricsdataintelligence
host: localhost
port: 3306
url: '%env(resolve:DATABASE_DATA_INTELLIGENCE_URL)%'
driver: "pdo_mysql"
driver_class: AppDBALMyDatabaseDriver
charset: utf8mb4
server_version: '15'
orm:
default_entity_manager: data_processing
entity_managers:
data_metier:
connection: data_metier
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'AppEntity'
alias: App
data_processing:
connection: data_processing
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'AppEntity'
alias: App
data_intelligence:
connection: data_intelligence
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'AppEntity'
alias: App
when@test:
doctrine:
dbal:
# "TEST_TOKEN" is typically set by ParaTest
dbname_suffix: "_test%env(default::TEST_TOKEN)%"
when@prod:
doctrine:
orm:
auto_generate_proxy_classes: false
proxy_dir: "%kernel.build_dir%/doctrine/orm/Proxies"
entity_managers:
data_metier:
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
data_processing:
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
data_intelligence:
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system
`
My security.yaml :
`security:
enable_authenticator_manager: true
password_hashers:
SymfonyComponentSecurityCoreUserPasswordAuthenticatedUserInterface: 'auto'
AppEntityUser:
algorithm: auto
providers:
data_metier_provider: # Le provider pour "S.I Processing"
entity:
class: AppEntityUser
property: user
manager_name: data_metier
data_processing_provider: # Le provider pour "Data Processing"
entity:
class: AppEntityUser
property: user
manager_name: data_processing
data_intelligence_provider: # Le provider pour "Data Intelligence"
entity:
class: AppEntityUser
property: user
manager_name: data_intelligence
app_users:
chain:
providers: ['data_metier_provider', 'data_processing_provider', 'data_intelligence_provider']
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login_data_processing:
pattern: ^/api/login_check_data_processing
logout: ~
stateless: true
json_login:
provider: data_processing_provider
check_path: /api/login_check_data_processing
username_path: user_formLogin
password_path: password_formLogin
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
switch_user:
provider: data_processing_provider
context: doctrine.security.switch_user.data_processing.target_path
login_data_intelligence:
pattern: ^/api/login_check_data_intelligence
logout: ~
stateless: true
json_login:
provider: data_intelligence_provider
check_path: /api/login_check_data_intelligence
username_path: user_formLogin
password_path: password_formLogin
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
switch_user:
provider: data_intelligence_provider
context: doctrine.security.switch_user.data_intelligence.target_path
login:
pattern: ^/api/login_check
stateless: true
logout: ~
json_login:
provider: data_metier_provider
check_path: /api/login_check
username_path: user_formLogin
password_path: password_formLogin
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
switch_user:
provider: data_metier_provider
context: doctrine.security.switch_user.data_metier.target_path
api:
pattern: ^/api
stateless: true
jwt:
provider: app_users
# 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: ^/api/login_check_data_processing, roles: PUBLIC_ACCESS }
- { path: ^/api/login_check_data_intelligence, roles: PUBLIC_ACCESS }
- { path: ^/api/login_check, roles: PUBLIC_ACCESS }
- { path: ^/api/login/connect, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
when@test:
security:
password_hashers:
SymfonyComponentSecurityCoreUserPasswordAuthenticatedUserInterface:
algorithm: auto
cost: 4 # Lowest possible value for bcrypt
time_cost: 3 # Lowest possible value for argon
memory_cost: 10 # Lowest possible value for argon`
Connections to the different databases work correctly, and my URLs are defined in the .env file. However, the problem lies in the mapping between my providers and my connections. No matter which provider is used (based on the route and firewall), the mapping with the connections in doctrine.yaml always selects the first connection defined in the configuration (for example, the data_metier connection). If I reverse the order of my connections, it selects the one that comes first. Thus, I can manually change databases via the code. But regardless of the choice made by the user, they are always connected to the same database.
So, I would like to know if my configuration contains an error. Is there a best practice for mapping a security provider to a database connection in Symfony? Is it possible to explicitly configure which provider should use which connection? I am also open to other approaches than using the “manager_name” option, as it seems to make no difference at all.
Overall, I’ve attempted to resolve the issues by modifying variable names in cases where a particular character might have caused a problem. I’ve also explored the option of using a single route and thus a single firewall, which I would have linked to the three providers using the “app_users” chain. Although my expectations were low, I still tried this approach. Additionally, I’ve tried to specify as many options as possible in my configurations, in case something was overlooked. Despite all these attempts, I find myself at a standstill and not sure which direction to take next.
I have been facing this issue for several days without finding a solution. I appreciate your help in advance.