If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeWe've added two options when we make the request: a Content-Type
header, to tell API Platform that any data we send will be JSON-formatted, and a json
option set to an empty array which is enough to at least send some valid JSON in the body of the request.
Because, before we added the json
option, we got this error: a 400 Bad Request with... some details in the body that indicate we sent invalid JSON.
But... wait a second. Assuming our access_control
security is set up correctly... shouldn't we get denied access before API Platform tries to deserialize the JSON data we're sending to the endpoint?
This is a little bit of a security gotcha... and even as I'm recording this, it looks like API Platform may change how this works in their next version. Progress!
When you send data to an endpoint API platform does the following things in this order. First, it deserializes the JSON into whatever resource object we're working with - like a CheeseListing
object. Second, it applies the security access controls. And third it applies our validation rules.
Do you see the problem? It's subtle. If API Platform has any problems deserializing the JSON into the object, the user of our API will see an error about that... even if that user doesn't have access to perform the operation. The JSON syntax error is one example of this. But there are other examples, like if you send a badly-formatted date string to a date field, you'll get a normalization error about this... even if you don't have access to that operation.
This is probably not a huge deal in most cases, but it is possible for a user to get some details about how your endpoints work... even if that user don't have access to them. Of course... they still can't do anything with those endpoints... but I do want you to be aware of this.
But... at this very moment, there's a pull request open on API Platform to rename access_control
to something else - probably security
- and to change the behavior so that security runs before deserialization. In other words, if this does concern you, it's likely to not behave like this in the future.
Ok, but now that we are sending valid JSON, let's see if the test passes! Run:
php bin/phpunit
And... we've got it! Green!
We've proven that you do need to log in to execute this operation. So now... let's log in and make sure it works!
To do that, we first need to put a user in the database. Cool! We got this: $user = new User()
and then fill in the email setEmail()
and username with setUsername()
. The only other field that's required on the user is the password
. Remember, that field is the encoded password. For now, let's cheat and generate an encoded password manually. Find your terminal and, once again, run:
php bin/console security:encode-password
Let's pass this foo
and... it gives me this giant, encoded password string. Copy that, and paste it into setPassword()
.
... lines 1 - 7 | |
class CheeseListingResourceTest extends ApiTestCase | |
{ | |
public function testCreateCheeseListing() | |
{ | |
... lines 12 - 18 | |
$user = new User(); | |
$user->setEmail('cheeseplease@example.com'); | |
$user->setUsername('cheeseplease'); | |
$user->setPassword('$argon2id$v=19$m=65536,t=6,p=1$AIC3IESQ64NgHfpVQZqviw$1c7M56xyiaQFBjlUBc7T0s53/PzZCjV56lbHnhOUXx8'); | |
... lines 23 - 35 | |
} | |
} |
The User
object is ready! To save this to the database, it's the same as being inside our code: we need to get the entity manager, then call persist and flush on it. But, normally, to get the entity manager - or any service - we use autowiring. Tests are the one place where autowiring doesn't work... because you're, sort of, "outside" of your application.
Instead, we'll fetch the services from the container by their ids. Try this: $em = self::$container
- a parent class sets the container on this nice property - ->get()
and the service id. Use doctrine
then say ->getManager()
.
You can also use the type-hint you use for autowiring as the service id. In other words, self::$container->get(EntityManagerInterface::class)
would work super well. And actually... it's probably a bit simpler than what I did.
Anyways, now that we have the entity manager, use the famous: $em->persist($user)
and $em->flush()
.
... lines 1 - 9 | |
public function testCreateCheeseListing() | |
{ | |
... lines 12 - 23 | |
$em = self::$container->get('doctrine')->getManager(); | |
$em->persist($user); | |
$em->flush(); | |
... lines 27 - 35 | |
} | |
... lines 37 - 38 |
Hey! We've got a user in the database! To test if an authenticated user can create a cheese listing... um... how can we authenticate as this user? Well, because we're using traditional session-based authentication... we just need to log in! Make a POST
request to /login
. I'll keep the header, but this time we will send some JSON data: email
set to cheeseplease@example.com
and password => 'foo'
.
... lines 1 - 9 | |
public function testCreateCheeseListing() | |
{ | |
... lines 12 - 27 | |
$client->request('POST', '/login', [ | |
'headers' => ['Content-Type' => 'application/json'], | |
'json' => [ | |
'email' => 'cheeseplease@example.com', | |
'password' => 'foo' | |
], | |
]); | |
... line 35 | |
} | |
... lines 37 - 38 |
And we should probably assert that this worked. Copy the response status code assertion, paste it down here, and check that this returns 204... because 204 is what we decided to return from SecurityController
.
... lines 1 - 34 | |
$this->assertResponseStatusCodeSame(204); | |
... lines 36 - 38 |
We're not quite yet making an authenticated request to create a new CheeseListing
... but let's check our progress! Find your terminal and run:
php bin/phpunit
Got it! Woo! We're now logged in and ready to start making authenticated requests.
Except... if you've done functional tests before... you might see a problem. Try running the tests again:
php bin/phpunit
Explosion!
Duplicate entry
cheeseplease@example.com
coming from the database. The most annoying thing about functional tests is that you need to control what's in the database... including what might be "left over" in the database from a previous test. This is nothing specific to API Platform... though the API Platform team does have some tools to help with this.
Next, let's guarantee that the database is in a clean state before each test is executed.
Just FYI, we added a note about it a bit earlier when we're talking about "access_control" for the first time: https://symfonycasts.com/sc...
Cheers!
Just an FYI:
There is a temporal coupling.
To access the container you need to execute the bootKernel()
method first (inherited from KernelTestCase)
The createClient()
method calls bootKernel()
A look at the bootKernel()
method will make clear that the kernel is shut down and rebooted again at each createClient()
call. This is probably because the create client method takes kernel options as the first argument.
A question:
I see that the \ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase
is still marked as <b>experimental</b> in the latest api-platform/core
and the last commit was <b>2 years ago</b>.
Does anyone recommend, or uses this in production?
Thanks in advance.
Hey Evozon S.
That's a good question, to be honest I'm not sure why it's still experimental, I'd say it's a good testing library that you can use in your projects but would be better if someone from the ApiPlatform project can confirm it. Perhaps you could ask this question here? https://github.com/api-plat...
Cheers!
Hi,
I am getting an error when trying to login via the json_login and I can't get what's the problem. Every time I get a 401.
$client->request('POST', "/login", [
'headers' => ['Content-Type' => 'application/json'],
'json' => [
'email' => 'a@yahoo.com',
'password' => 'foo'
],
]);
$this->assertResponseStatusCodeSame(204);
PHPUnit 8.5.14 by Sebastian Bergmann and contributors.
Testing Project Test Suite
F 1 / 1 (100%)
Time: 667 ms, Memory: 26.00 MB
There was 1 failure:
1) App\Tests\Functional\CheeseListingResourceTest::testCreateCheeseListing
Failed asserting that the Response status code is 204.
HTTP/1.1 401 Unauthorized
Cache-Control: no-cache, private
Content-Type: application/json
Date: Mon, 01 Mar 2021 13:50:45 GMT
Link: <http://example.com/api/docs.jsonld>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"
X-Robots-Tag: noindex
{"error":"Authentication request could not be processed due to a system problem."}
/Users/api-2/src/ApiPlatform/Test/BrowserKitAssertionsTrait.php:47
/Users/api-2/tests/Functional/CheeseListingResourceTest.php:31
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
Hey @Max
Did you configure your firewall correctly?
// config/packages/security.yaml
security:
firewalls:
main:
anonymous: true
json_login:
check_path: app_login
username_path: email
password_path: password
and the User you're trying to log in exists in the database?
Cheers!
I got stuck with Symfony 5.2 (and even with Api Plaform ^v2.6.0-alpha.1)...
I can login succesfully...
$this->logIn($client, 'admin', 'admin@123');
... but in the next call ...
`
$client->request('POST', '/api/convenio',
[
'headers' => ['Content-Type' => 'application/ld+json'],
'json' => [
'nome' => 'CONVENIO ' . rand(0, 9999),
'ativo' => true
]
]);
`
It always returns <b>HTTP/1.1 401 Unauthorized</b>.
Debugging, I can see that the security-core/Authorization/Voter/RoleVoter.php always receives a Symfony\Component\Security\Core\Authentication\Token\AnonymousToken
DEBUG:
`Symfony\Component\Security\Core\Authentication\Token\AnonymousToken Object
(
[secret:Symfony\Component\Security\Core\Authentication\Token\AnonymousToken:private] => PPgg3IR
[user:Symfony\Component\Security\Core\Authentication\Token\AbstractToken:private] => <b>anon.</b>
[roleNames:Symfony\Component\Security\Core\Authentication\Token\AbstractToken:private] => Array
(
)
[authenticated:Symfony\Component\Security\Core\Authentication\Token\AbstractToken:private] => 1
[attributes:Symfony\Component\Security\Core\Authentication\Token\AbstractToken:private] => Array
(
)
)
`
It seems that the ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Client doesn't keep the authentication between the calls.
Here's my config/packages/test/security.yaml
`
security:
encoders:
CrosierSource\CrosierLibBaseBundle\Entity\Security\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
app_user_provider:
entity:
class: CrosierSource\CrosierLibBaseBundle\Entity\Security\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
test:
anonymous: true
lazy: true
json_login:
check_path: /testingApiLogin
username_path: username
password_path: password
logout:
path: app_logout
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# but, definitely allow /login to be accessible anonymously
# - { path: ^/testingApiLogin, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# if you wanted to force EVERY URL to be protected
# - { path: ^/, roles: IS_AUTHENTICATED_REMEMBERED }
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
`
Almost two days up on this now :-(
Hey @Carlos!
Sorry for my slow reply! This is super frustrating! First, I don’t see any obvious problems. To debug, I want to initially determine if this is a problem with the test not keeping authentication (this is possible, though I’m not familiar with cases of this happening) or if there is something bigger wrong and you lose authentication even in a browser.
To test this, I would write a tiny bit of javascript that does the same thing as your test: sends an ajax request to log in, then sends another ajax request to the api endpoint. Heck, you could even just make an ajax call to the login endpoint and then refresh the page. If everything is working, the web debug toolbar will show you authenticated. There *are* some reasons why you could lose authentication immediately after logging in, which is why I’d check this first.
Also, do you have any special cookie settings? Probably not, but I thought I’d ask just in case.
Cheers!
I followed your awesome cheese course up until this point but got stuck at this unauthenticated test case.
I am using the new `security` attribute in the collection operation post object: `"post"={"security"="is_granted('ROLE_ADMIN')"}`. Instead of producing status code 401 I get error code 500 with the message "The current token must be set to use the "security" attribute (is the URL behind a firewall?)." at `/Users/ff/Projects/Kescht/kescht/vendor/api-platform/core/src/Security/ResourceAccessChecker.php` line 52. The token seems to be missing. But is there a token for a not authenticated user?
Hey Fränz F.!
Are you using the new Symfony 5.1/5.2 security - the enable_authenticator_manager
option inside of security.yaml? If so, there is a bug in it with API Platform. Or, to be more fair to them, API Platform needed to make an update to fully support the new security system ... and they HAVE - https://github.com/api-platform/core/pull/3899 - but it has not been released yet. It should be released in 2.6.0 - which is currently at BETA 1 (and the fix IS included in that release).
If you are NOT using the new security system and I am TOTALLY wrong, please let me know ;).
Cheers!
Hi weaverryan! Yes, indeed, I am using the new authentication manager. I completely missed this incompatibility issue with it. After upgrading to the 2.6.0-beta.1 version of api-platform/core all tests are passing and I get lovely 401 error messages. Awesome! Thank you!
Does ther a way to authenticate the user with a fictive token ? I already tried this example https://symfony.com/doc/cur... but unforthenlly the client of api test case does not have getCookieJar() function. any idea !
Hey ahmedbhs!
Sorry for the late reply! Yes, this should indeed be possible :). In API Platform, they use their own custom Client service (as you've noticed). But inside that services is the "normal" one. So, try this:
$client = self::createClient();
$client->getKernelBrowser()->getCookieJar()->set(...);
Let me know if that works!
Cheers!
I have a weird issue:
`public function testCreatePostAuthorized(): void
{
$client = self::createClient();
$client->request(
'POST',
'/login',
[
'json' => [
'email' => 'gab@gmail.com',
'password' => '123123'
]
]
);
self::assertResponseStatusCodeSame(204);
$client->request(
'POST',
'/api/posts'
);
self::assertResponseStatusCodeSame(200);
}`
The login assert works fine I get 204, but the 2nd request I still get 401. Seems that the client object is maybe not storing the session id?
Seems the issue was because I had setup a session.cookie_domain = 'symfony.localhost' inside the framework.yaml
Yes removing that fixed the issue, but that was there because I had a different issue when trying to login from my front end angular project, I will see later if there's a way to fix both issues.
Hello SfCasts team, i have no problem with this actual version of the app. But when i do a composer update and i just relaunch the tests, i have this huge error:
App\Tests\Functional\UserResourceTest::testCreateUser
Failed asserting that the Response status code is 201.
HTTP/1.1 401 Unauthorized
Cache-Control: no-cache, private
Content-Type: application/ld+json; charset=utf-8
Date: Mon, 16 Dec 2019 20:18:38 GMT
Link: <http://example.com/api/docs.jsonld>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"
X-Content-Type-Options: nosniff
X-Frame-Options: deny
X-Robots-Tag: noindex
{"@context":"\/api\/contexts\/Error","@type":"hydra:Error","hydra:title":"An error occurred","hydra:description":"Full authentication is required to access this resource.","trace":[{"namespace":"","short_class":"","class":"","type":"","function":"","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/security-http\/Firewall\/ExceptionListener.php","line":187,"args":[]},{"namespace":"Symfony\\Component\\Security\\Http\\Firewall","short_class":"ExceptionListener","class":"Symfony\\Component\\Security\\Http\\Firewall\\ExceptionListener","type":"-\u003E","function":"startAuthentication","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/security-http\/Firewall\/ExceptionListener.php","line":142,"args":[["object","Symfony\\Component\\HttpFoundation\\Request"],["object","Symfony\\Component\\Security\\Core\\Exception\\InsufficientAuthenticationException"]]},{"namespace":"Symfony\\Component\\Security\\Http\\Firewall","short_class":"ExceptionListener","class":"Symfony\\Component\\Security\\Http\\Firewall\\ExceptionListener","type":"-\u003E","function":"handleAccessDeniedException","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/security-http\/Firewall\/ExceptionListener.php","line":101,"args":[["object","Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent"],["object","Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException"]]},{"namespace":"Symfony\\Component\\Security\\Http\\Firewall","short_class":"ExceptionListener","class":"Symfony\\Component\\Security\\Http\\Firewall\\ExceptionListener","type":"-\u003E","function":"onKernelException","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/event-dispatcher\/Debug\/WrappedListener.php","line":126,"args":[["object","Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent"],["string","kernel.exception"],["object","Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"]]},{"namespace":"Symfony\\Component\\EventDispatcher\\Debug","short_class":"WrappedListener","class":"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener","type":"-\u003E","function":"__invoke","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php","line":260,"args":[["object","Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent"],["string","kernel.exception"],["object","Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"]]},{"namespace":"Symfony\\Component\\EventDispatcher","short_class":"EventDispatcher","class":"Symfony\\Component\\EventDispatcher\\EventDispatcher","type":"-\u003E","function":"doDispatch","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php","line":235,"args":[["array",[["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"]]],["string","kernel.exception"],["object","Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent"]]},{"namespace":"Symfony\\Component\\EventDispatcher","short_class":"EventDispatcher","class":"Symfony\\Component\\EventDispatcher\\EventDispatcher","type":"-\u003E","function":"callListeners","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php","line":73,"args":[["array",[["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"],["object","Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"]]],["string","kernel.exception"],["object","Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent"]]},{"namespace":"Symfony\\Component\\EventDispatcher","short_class":"EventDispatcher","class":"Symfony\\Component\\EventDispatcher\\EventDispatcher","type":"-\u003E","function":"dispatch","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/event-dispatcher\/Debug\/TraceableEventDispatcher.php","line":168,"args":[["object","Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent"],["string","kernel.exception"]]},{"namespace":"Symfony\\Component\\EventDispatcher\\Debug","short_class":"TraceableEventDispatcher","class":"Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher","type":"-\u003E","function":"dispatch","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/http-kernel\/HttpKernel.php","line":222,"args":[["object","Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent"],["string","kernel.exception"]]},{"namespace":"Symfony\\Component\\HttpKernel","short_class":"HttpKernel","class":"Symfony\\Component\\HttpKernel\\HttpKernel","type":"-\u003E","function":"handleException","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/http-kernel\/HttpKernel.php","line":79,"args":[["object","Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException"],["object","Symfony\\Component\\HttpFoundation\\Request"],["integer",1]]},{"namespace":"Symfony\\Component\\HttpKernel","short_class":"HttpKernel","class":"Symfony\\Component\\HttpKernel\\HttpKernel","type":"-\u003E","function":"handle","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/http-kernel\/Kernel.php","line":198,"args":[["object","Symfony\\Component\\HttpFoundation\\Request"],["integer",1],["boolean",true]]},{"namespace":"Symfony\\Component\\HttpKernel","short_class":"Kernel","class":"Symfony\\Component\\HttpKernel\\Kernel","type":"-\u003E","function":"handle","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/http-kernel\/Client.php","line":68,"args":[["object","Symfony\\Component\\HttpFoundation\\Request"],["integer",1],["boolean",true]]},{"namespace":"Symfony\\Component\\HttpKernel","short_class":"Client","class":"Symfony\\Component\\HttpKernel\\Client","type":"-\u003E","function":"doRequest","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/framework-bundle\/Client.php","line":131,"args":[["object","Symfony\\Component\\HttpFoundation\\Request"]]},{"namespace":"Symfony\\Bundle\\FrameworkBundle","short_class":"Client","class":"Symfony\\Bundle\\FrameworkBundle\\Client","type":"-\u003E","function":"doRequest","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/browser-kit\/Client.php","line":407,"args":[["object","Symfony\\Component\\HttpFoundation\\Request"]]},{"namespace":"Symfony\\Component\\BrowserKit","short_class":"Client","class":"Symfony\\Component\\BrowserKit\\Client","type":"-\u003E","function":"request","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/src\/ApiPlatform\/Test\/Client.php","line":124,"args":[["string","POST"],["string","http:\/\/example.com\/api\/users"],["array",[]],["array",[]],["array",{"HTTP_USER_AGENT":["string","Symfony BrowserKit"],"HTTP_ACCEPT":["string","application\/ld+json"],"CONTENT_TYPE":["string","application\/json"],"HTTP_HOST":["string","example.com"],"HTTPS":["boolean",false]}],["string","{\u0022email\u0022:\u0022cheeseplease@example.com\u0022,\u0022username\u0022:\u0022cheeseplease\u0022,\u0022password\u0022:\u0022brie\u0022}"]]},{"namespace":"App\\ApiPlatform\\Test","short_class":"Client","class":"App\\ApiPlatform\\Test\\Client","type":"-\u003E","function":"request","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/tests\/Functional\/UserResourceTest.php","line":19,"args":[["string","POST"],["array",{"scheme":["string","http:"],"authority":["string","\/\/example.com"],"path":["string","\/api\/users"],"query":["null",null],"fragment":["null",null]}],["array",{"normalized_headers":["array",{"accept":["array",[["string","accept: application\/ld+json"]]],"content-type":["array",[["string","Content-Type: application\/json"]]]}],"headers":["array",[["string","accept: application\/ld+json"],["string","Content-Type: application\/json"]]],"query":["array",[]],"body":["string","{\u0022email\u0022:\u0022cheeseplease@example.com\u0022,\u0022username\u0022:\u0022cheeseplease\u0022,\u0022password\u0022:\u0022brie\u0022}"],"base_uri":["array",{"scheme":["string","http:"],"authority":["string","\/\/example.com"],"path":["null",null],"query":["null",null],"fragment":["null",null]}],"http_version":["null",null],"timeout":["float",60]}]]},{"namespace":"App\\Tests\\Functional","short_class":"UserResourceTest","class":"App\\Tests\\Functional\\UserResourceTest","type":"-\u003E","function":"testCreateUser","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/Framework\/TestCase.php","line":1329,"args":[]},{"namespace":"PHPUnit\\Framework","short_class":"TestCase","class":"PHPUnit\\Framework\\TestCase","type":"-\u003E","function":"runTest","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/Framework\/TestCase.php","line":949,"args":[]},{"namespace":"PHPUnit\\Framework","short_class":"TestCase","class":"PHPUnit\\Framework\\TestCase","type":"-\u003E","function":"runBare","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/Framework\/TestResult.php","line":680,"args":[]},{"namespace":"PHPUnit\\Framework","short_class":"TestResult","class":"PHPUnit\\Framework\\TestResult","type":"-\u003E","function":"run","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/Framework\/TestCase.php","line":678,"args":[["object","App\\Tests\\Functional\\UserResourceTest"]]},{"namespace":"PHPUnit\\Framework","short_class":"TestCase","class":"PHPUnit\\Framework\\TestCase","type":"-\u003E","function":"run","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/Framework\/TestSuite.php","line":568,"args":[["object","PHPUnit\\Framework\\TestResult"]]},{"namespace":"PHPUnit\\Framework","short_class":"TestSuite","class":"PHPUnit\\Framework\\TestSuite","type":"-\u003E","function":"run","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/Framework\/TestSuite.php","line":568,"args":[["object","PHPUnit\\Framework\\TestResult"]]},{"namespace":"PHPUnit\\Framework","short_class":"TestSuite","class":"PHPUnit\\Framework\\TestSuite","type":"-\u003E","function":"run","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/TextUI\/TestRunner.php","line":619,"args":[["object","PHPUnit\\Framework\\TestResult"]]},{"namespace":"PHPUnit\\TextUI","short_class":"TestRunner","class":"PHPUnit\\TextUI\\TestRunner","type":"-\u003E","function":"doRun","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/TextUI\/Command.php","line":201,"args":[["object","PHPUnit\\Framework\\TestSuite"],["array",{"listGroups":["boolean",false],"listSuites":["boolean",false],"listTests":["boolean",false],"listTestsXml":["boolean",false],"loader":["null",null],"useDefaultConfiguration":["boolean",true],"loadedExtensions":["array",[]],"notLoadedExtensions":["array",[]],"colors":["string","always"],"testSuffixes":["array",[["string","Test.php"],["string",".phpt"]]],"configuration":["object","PHPUnit\\Util\\Configuration"],"listeners":["array",[["object","Symfony\\Bridge\\PhpUnit\\Legacy\\SymfonyTestsListenerForV7"]]],"debug":["boolean",false],"filter":["boolean",false],"backupGlobals":["boolean",false],"bootstrap":["string","\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/config\/bootstrap.php"],"testdoxGroups":["array",[]],"testdoxExcludeGroups":["array",[]],"addUncoveredFilesFromWhitelist":["boolean",true],"backupStaticAttributes":["null",null],"beStrictAboutChangesToGlobalState":["null",null],"beStrictAboutResourceUsageDuringSmallTests":["boolean",false],"cacheResult":["boolean",true],"cacheTokens":["boolean",false],"columns":["integer",80],"convertDeprecationsToExceptions":["boolean",true],"convertErrorsToExceptions":["boolean",true],"convertNoticesToExceptions":["boolean",true],"convertWarningsToExceptions":["boolean",true],"crap4jThreshold":["integer",30],"disallowTestOutput":["boolean",false],"disallowTodoAnnotatedTests":["boolean",false],"defaultTimeLimit":["integer",0],"enforceTimeLimit":["boolean",false],"excludeGroups":["array",[]],"executionOrder":["integer",0],"executionOrderDefects":["integer",0],"failOnRisky":["boolean",false],"failOnWarning":["boolean",false],"groups":["array",[]],"noInteraction":["boolean",false],"processIsolation":["boolean",false],"processUncoveredFilesFromWhitelist":["boolean",false],"randomOrderSeed":["integer",1576527511],"registerMockObjectsFromTestArgumentsRecursively":["boolean",false],"repeat":["boolean",false],"reportHighLowerBound":["integer",90],"reportLowUpperBound":["integer",50],"reportUselessTests":["boolean",true],"reverseList":["boolean",false],"resolveDependencies":["boolean",true],"stopOnError":["boolean",false],"stopOnFailure":["boolean",false],"stopOnIncomplete":["boolean",false],"stopOnRisky":["boolean",false],"stopOnSkipped":["boolean",false],"stopOnWarning":["boolean",false],"stopOnDefect":["boolean",false],"strictCoverage":["boolean",false],"timeoutForLargeTests":["integer",60],"timeoutForMediumTests":["integer",10],"timeoutForSmallTests":["integer",1],"verbose":["boolean",false],"cacheResultFile":["string","\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish"]}],["boolean",true]]},{"namespace":"PHPUnit\\TextUI","short_class":"Command","class":"PHPUnit\\TextUI\\Command","type":"-\u003E","function":"run","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/src\/TextUI\/Command.php","line":160,"args":[["array",[["string","bin\/phpunit"],["string","--colors=always"]]],["boolean",true]]},{"namespace":"PHPUnit\\TextUI","short_class":"Command","class":"PHPUnit\\TextUI\\Command","type":"::","function":"main","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/phpunit","line":17,"args":[]},{"namespace":"","short_class":"","class":"","type":"","function":"include","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/phpunit-bridge\/bin\/simple-phpunit.php","line":293,"args":[["string","\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/.phpunit\/phpunit-8.2-0\/phpunit"]]},{"namespace":"","short_class":"","class":"","type":"","function":"require","file":"\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/bin\/phpunit","line":13,"args":[["string","\/Users\/casrime\/Downloads\/code-api-platform-security (3)\/finish\/vendor\/symfony\/phpunit-bridge\/bin\/simple-phpunit.php"]]}]}
/Users/casrime/Downloads/code-api-platform-security (3)/finish/src/ApiPlatform/Test/BrowserKitAssertionsTrait.php:47
/Users/casrime/Downloads/code-api-platform-security (3)/finish/tests/Functional/UserResourceTest.php:24
Do you have the same problem ?
Thanks you 😉
Hey Abdelkarim,
Yeah, more info about it: It seems access_control was replaced with security. see their changelog: https://github.com/api-plat...
So, if you replace "access_control" with security in User object - it should pass :)
Cheers!
Hey Abdelkarim,
Yes, the new minor version of "api-platform/core" causes it to fail. If you downgrade it to "2.4.*" - it will work. So, it sounds like a BC break in 2.5.* but I'm not sure what exactly causes it though.
Cheers!
Hey Victor,
I think it comes from Symfony and not API Platform, like this issue : https://github.com/symfony/...
Cheers 😉
Ohh, that's interesting and it seems very likely that that's the problem. You may want to try what they recommend, downgrading to Symfony 4.3.8 and see if it works, meanwhile we wait for a patch fix :)
Cheers!
Hello MolloKhan , i solved my problem. It was because i used an old version of User entity (which implements UserInterface), with a roles field, who had an array type. I change it to a json type and now everything is good.
Cheers 😉
I also add my composer.json :
{
"type": "project",
"license": "proprietary",
"require": {
"php": "^7.1.3",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/api-pack": "^1.2",
"doctrine/doctrine-migrations-bundle": "^2.0",
"nesbot/carbon": "^2.17",
"symfony/console": "4.3.*",
"symfony/dotenv": "4.3.*",
"symfony/flex": "^1.1",
"symfony/framework-bundle": "4.3.*",
"symfony/http-client": "4.3.*",
"symfony/monolog-bundle": "^3.4",
"symfony/webpack-encore-bundle": "^1.6",
"symfony/yaml": "4.3.*"
},
"config": {
"preferred-install": {
"*": "dist"
},
"sort-packages": true,
"platform": {
"php": "7.2.0"
}
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"replace": {
"paragonie/random_compat": "2.*",
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php71": "*",
"symfony/polyfill-php70": "*",
"symfony/polyfill-php56": "*"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
]
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "4.3.*"
}
},
"require-dev": {
"hautelook/alice-bundle": "^2.5",
"symfony/maker-bundle": "^1.11",
"symfony/profiler-pack": "^1.0",
"symfony/test-pack": "^1.0"
}
}
// composer.json
{
"require": {
"php": "^7.1.3, <8.0",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^2.1", // v2.4.5
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.13.2
"doctrine/doctrine-bundle": "^1.6", // 1.11.2
"doctrine/doctrine-migrations-bundle": "^2.0", // v2.0.0
"doctrine/orm": "^2.4.5", // v2.7.2
"nelmio/cors-bundle": "^1.5", // 1.5.6
"nesbot/carbon": "^2.17", // 2.21.3
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0", // 4.3.1
"symfony/asset": "4.3.*", // v4.3.2
"symfony/console": "4.3.*", // v4.3.2
"symfony/dotenv": "4.3.*", // v4.3.2
"symfony/expression-language": "4.3.*", // v4.3.2
"symfony/flex": "^1.1", // v1.18.7
"symfony/framework-bundle": "4.3.*", // v4.3.2
"symfony/http-client": "4.3.*", // v4.3.3
"symfony/monolog-bundle": "^3.4", // v3.4.0
"symfony/security-bundle": "4.3.*", // v4.3.2
"symfony/twig-bundle": "4.3.*", // v4.3.2
"symfony/validator": "4.3.*", // v4.3.2
"symfony/webpack-encore-bundle": "^1.6", // v1.6.2
"symfony/yaml": "4.3.*" // v4.3.2
},
"require-dev": {
"hautelook/alice-bundle": "^2.5", // 2.7.3
"symfony/browser-kit": "4.3.*", // v4.3.3
"symfony/css-selector": "4.3.*", // v4.3.3
"symfony/maker-bundle": "^1.11", // v1.12.0
"symfony/phpunit-bridge": "^4.3", // v4.3.3
"symfony/stopwatch": "4.3.*", // v4.3.2
"symfony/web-profiler-bundle": "4.3.*" // v4.3.2
}
}
The PR you mentioned has been merged and released in v2.5.0 of api-platform: https://github.com/api-plat..., and it seems that all it takes to switch to the fixed method is to use `security` instead of `access_control` in annotations.