How can I test a page behind a security firewall in Symfony? - symfony

In Symfony's cookbook, there is a page entitled How to Simulate Authentication with a Token in a Functional Test. In it, it is said that:
The technique described in How to Simulate HTTP Authentication in a Functional Test is cleaner and therefore the preferred way.
Also, on the page that the quotation above links to, the documentation says:
The trick is to include the http_basic key in your firewall, along with the form_login key
This tells me that it is all right to have the form_login key, along with the http_basic key, and somehow http_basic should take precedence.
Here is my config_test.yml configuration file:
imports:
- { resource: config_dev.yml }
framework:
test: ~
session:
storage_id: session.storage.mock_file
profiler:
collect: false
web_profiler:
toolbar: false
intercept_redirects: false
swiftmailer:
disable_delivery: true
liip_functional_test:
cache_sqlite_db: true
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_sqlite
path: %kernel.cache_dir%/test.db
security:
firewalls:
default:
http_basic: ~
However, when I open my application in the test environment, I still get redirected to the login_form URL.
Why isn't setting the http_basic acting like the documentation says it should, namely it getting activated rather than form_login?

As commented here, having the code I pasted in my original question works just fine. The reason it is loading the login form is because I am not logging in via http_basic. In other words, when I have both form_login and http_basic enabled, I can login both by providing the PHP_AUTH_USER/PHP_AUTH_PASSWORD, and by logging in through the form. In effect, I don't need different security_*.yml files; I just need to add http_basic: ~ to the already-defined firewall.

Just split security.yml to security_test.yml and security_prod.yml.
In security_test.yml put default security configuration (as delivered with Symfony) or other one, which doesn't have firewall restrictions.
Create a specific config file for test environment, like config_test.yml with
imports:
- { resource: config.yml }
- { resource: security_test.yml }
note here config.yml itself doesn't have any security imports, because You will receive some Exception about overriding security directive or smth.
Create a separate config_prod.yml with
imports:
- { resource: config.yml }
- { resource: security_prod.yml }
Now You have separate security for test and prod environments.
If Your environment naming is good, then Kernel will pick config_test.yml only when tests are executed. For development environment Your should use config_dev.yml instead of config_test.yml

Related

Access Denied for view and download routes in SonataMediaBundle and Symfony 4

I use Symfony 4 (more precise 4.1) with SonataAdminBundle and SonataMediaBundle.
This is my config/routes/sonata_media.yaml:
sonata_media_gallery:
resource: '#SonataMediaBundle/Resources/config/routing/gallery.xml'
prefix: /media/gallery
sonata_media:
resource: '#SonataMediaBundle/Resources/config/routing/media.xml'
prefix: /media
If I run php bin/console debug:router there are the following routes in the output:
sonata_media_gallery_index ANY ANY ANY /media/gallery/
sonata_media_gallery_view ANY ANY ANY /media/gallery/view/{id}
sonata_media_view ANY ANY ANY /media/view/{id}/{format}
sonata_media_download ANY ANY ANY /media/download/{id}/{format}
The first two routes work fine, but when I try the other two routes, for example:
http://localhost:8000/media/view/
http://localhost:8000/media/view/1/default
http://localhost:8000/media/download/1
http://localhost:8000/media/download/1/default
then I always get AccessDeniedException, even though I'm authenticated as ROLE_SUPER_ADMIN.
The error happens in vendor/sonata-project/media-bundle/src/Controller/MediaController.php in downloadAction and in viewAction. I was digging around in the source code, but can't find the reason for the exception thrown.
After some research I found the culprit and solved the problem. Here I'd like to share my knowledge.
As I mentioned in the question, the exceptions were thrown from:
vendor/sonata-project/media-bundle/src/Controller/MediaController.php
in the methods downloadAction and viewAction. It was the following if-condition:
if (!$this->get('sonata.media.pool')->getDownloadSecurity($media)->isGranted($media, $this->getCurrentRequest())) {
throw new AccessDeniedException();
}
which is present in both methods. This led me to vendor/sonata-project/media-bundle/src/Provider/Pool.php, and further to vendor/sonata-project/media-bundle/src/Security/RolesDownloadStrategy.php. I couldn't find any bug or problem there, but it opened my eyes to another position in my own configuration:
access_control:
- { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
- { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
How could I be so stupid? The path /media is not declared in security.yml and can be accessed by not authenticated users. The SonataMediaBundle requires per default ROLE_ADMIN or ROLE_SUPER_ADMIN for downloading/viewing the media.
The routes for the Gallery were accessible because vendor/sonata-project/media-bundle/src/Controller/GalleryController.php doesn't check if access is granted.
After finding the culprit the question was which approach to chose to solve the problem
1) Change the route prefix:
sonata_media:
resource: '#SonataMediaBundle/Resources/config/routing/media.xml'
prefix: /admin/media
The declared path in security.yml covers now the media and ROLE_ADMIN and ROLE_SUPER_ADMIN can access the routes.
Disadvantage: what if you want to expose the media outside of the admin? And what if other roles should be able to access them.
2) Declare a new path in security.yml:
access_control:
- { path: ^/media/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
Now we can expose the media outside of the admin. But the other issue is still there: what if other roles need to access the media?
3) Configure another download strategy in the config for SonataMedia:
sonata_media:
# ...
contexts:
default: # the default context is mandatory
download:
strategy: sonata.media.security.connected_strategy
mode: http
# ...
and adjust the path:
access_control:
# ...
- { path: ^/media/, role: [IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED] }
# ...
Now every logged in user can access the media. This solution worked for me.
However it is not a one-size-fits-all recipe. Please check the chapter security from the official documentation to get more detailed information.

Empty dashboard for not ROLE_SUPER_ADMIN user (symfony2, sonata acl)

I use ACL in Sonata Admin Bundle. Аnd when I log in as a root (which has ROLE_SUPER_ADMIN) I can create new users. I've created one (named qwer) and then loged in as qwer.
PROBLEM: in my situation qwer user has empty dashbord, even having roles like
ROLE_SONATA_USER_ADMIN_USER_GUEST, ROLE_SONATA_USER_ADMIN_USER_STAFF, ROLE_SONATA_USER_ADMIN_USER_EDITOR
Please tell my -- what should I do to understad where the problem is.
Did you follow the documentation for ACL fully? You should add your sonata_admin configuration and security.yml just to be sure. Mine looks like:
sonata_admin:
# ...
security:
handler: sonata.admin.security.handler.acl
# acl security information
information:
LIST: [LIST]
GUEST: [VIEW, LIST]
STAFF: [LIST, CREATE]
EDITOR: [OPERATOR, EXPORT, EDIT]
ADMIN: [MASTER]
admin_permissions: [CREATE, LIST, DELETE, UNDELETE, EXPORT, OPERATOR, MASTER]
# permission related to the objects
object_permissions: [VIEW, EDIT, DELETE, UNDELETE, OPERATOR, MASTER, OWNER]
Also ensure your security.yml has the required configuration:
security:
# ...
providers:
fos_userbundle:
id: fos_user.user_manager
acl:
connection: default
access_decision_manager:
strategy: unanimous
And add a PermissionMap to your app/config/parameters.yml or bundle parameters:
# src/AppBundle/Resources/config/services.yml
parameters:
# ...
# Symfony 3 and above
security.acl.permission.map:
class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap
# Symfony < 3
security.acl.permission.map.class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap
Then there are 3 commands you will need to run:
Initialize your ACL setup (only once)
php app/console init:acl
Reload changes to the configuration (every change in the sonata_admin configuration file)
php app/console sonata:admin:setup-acl
To generate (new) ACL rules for already existing entities/objects. (every change in the sonata_admin configuration file)
php app/console sonata:admin:generate-object-acl
Then once the configuration is setup, logout and log back in again for the roles to apply.
to resolve this problem check that your have in of your app bundle lines:
services:
security.acl.permission.map:
class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap
parameters:

How to share the cookie with subdomains

I have over two subdomains in my site.
such as:www.example.com, login.example.com, user.example.com, cart.example.com...
I setup the cookie_domain as .example.com in config.yml and php.ini
when I setCookies('test', 'value', '.example.com'), but the cookie is always not shared in the subdomain.
there is my config.yml
session:
handler_id: session.handler.native_file
save_path: "%kernel.root_dir%/../var/sessions/"
cookie_domain: .example.com
cookie_lifetime: 0
name: TESTSESSIONID
in my security:
security:
session_fixation_strategy: none
i needed this to work for all subdomains, but the domain itself changed depending on whether i was developing or in production.
I use parameters_dev.yml and parameters.yml to define the 'domain', then added this in config.yml to allow cookies accross all subdomains.
framework:
session:
cookie_domain: '.%domain%'
You can configure the session key in the config.yml defining a cookie_domain. As example:
config.yml
session:
cookie_lifetime: 0
save_path: %kernel.root_dir%/var/sessions
cookie_domain: .my-domain.com
name: SFSESSID
Hope this help
You need to set trusted hosts in your application because for security reason symfony application will respond to whitelisted hosts and subdomins. To do this you have couple of ways to fix it
In your config.yml set trusted_hosts like below
#app/config/config.yml
framework:
trusted_hosts: ['example.com', 'login.example.com', 'user.example.com', 'cart.example.com']
You can also set the trusted hosts in the front controller using the Request::setTrustedHosts() method like below.
//web/app.php
Request::setTrustedHosts(array('.*\.?example.com$'));
Please find below documentation links are for reference purpose
reference 1
reference 2
reference 3

Symfony2 test parameters

Is there a way to configure test parameters with Symfony2?
I tried importing parameters_test.yml from the config_test.yml file but the variables still come from the parameters.yml file which is imported by config.yml which is imported by config_dev.yml which is imported by config_test.yml.
Basically I want to setup different variables in a test parameter file so I can access the values and also use them in the config files. If I hardcode the variables directly into config_test.yml I don't have access to them.
The variable I'm interested in particular is database_name or doctrine->dbal->dbname.
Thanks
Of course ensure your two first lines your config_test.yml are:
imports:
- { resource: config_dev.yml }
- { resource: parameters_test.yml }
In config_test.yml simple write this configuration:
# Doctrine Configuration
doctrine:
dbal:
driver: %database_driver_test%
host: %database_host_test%
port: %database_port_test%
dbname: %database_name_test%
user: %database_user_test%
password: %database_password_test%

Symfony2 configure DB storage sessions

UPDATE: problem solved, see the comments (many issues, the versions differences was but one of them).
I'm trying to configure sessions in Symfony2 in config.yml file. I have the following configuration:
session:
default_locale: %locale%
lifetime: 7200
auto_start: true
storage_id: session.storage.pdo
parameters:
pdo.db_options:
db_table: session
db_id_col: session_id
db_data_col: session_value
db_time_col: session_time
services:
pdo:
class: PDO
arguments:
- "mysql:dbname=%database_name%"
- %database_user%
- %database_password%
session.storage.pdo:
class: Symfony\Component\HttpFoundation\SessionStorage\PdoSessionStorage
arguments: [#pdo, %session.storage.options%, %pdo.db_options%]
It's based on Symfony2's cookbook http://symfony.com/doc/2.0/cookbook/configuration/pdo_session_storage.html
I've created exactly the same table as in the given link.
However, it doesn't work. I get some "blank" error (no error message, but "PDO Exception" and "Error Exception"). I admit I have no much knowledge on configuring the Symfony2 or any info (that's why I'm using cookbook). I lost a lot of time and see no much documentation about it in the internet, not mentioning the fact that internet is quite silent about this case (having session storaged to DB table in Symfony2).
My NetBeans is "shouting" sth about the last line:
arguments: [#pdo, %session.storage.options%, %pdo.db_options%]
"ScannerException while scanning for the next token we had this found character #(64) that cannot start any token".
UPDATE:
Hmm now I'm not sure if it's about the configuration. I can see that Symfony2's cookbook (use... ) example doesn't match actually the file structure in the Symfony2's bundle. In a word, there is no such file-path, but after putting the real one it still doesn't work.
I had the same problem as you with Symfony 2.5. Turns out my solution was to disable session.auto_start in php.ini. The PdoSessionStorage will not take over if the session is being started before PDO has the opportunity to take control of it. I had overlooked this at first because I was modifying the wrong one (I had two copies of php.ini). To check if this is the problem on yours, run this command:
echo ini_get('session.auto_start');
If that returns a '1' or a 'true', then be sure to set this in your php.ini:
session.auto_start = 0
So for reference, here is my setup with Symfony 2.5 to make this work:
config.yml
framework:
session:
handler_id: session.handler.pdo
parameters:
pdo.db_options:
db_table: session
db_id_col: session_id
db_data_col: session_value
db_time_col: session_time
services:
session.handler.pdo:
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
arguments: ["#pdo", "%pdo.db_options%"]
pdo:
class: PDO
arguments:
dsn: "mysql:host=%database_host%;port=%database_port%;dbname=%database_name%"
user: "%database_user%"
password: "%database_password%"
calls:
- [setAttribute, [3, 2]] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION

Resources