I am trying to run tests in my Symfony test environment but keep getting an error.
Error: Call to a member function getContainer() on null
I figured I have problem with authentication of the user.
I have loaded a fixture to persist one user object into test database fort the needs of authentications (even if i do not know it that is needed).
protected function createAuthenticatedClient(): KernelBrowser
{
self::ensureKernelShutdown();
$client = static::createClient();
$session = $this->client->getContainer()->get('session');
$firewall = 'secured_area';
$token = new UsernamePasswordToken('admin', null, $firewall, array('ROLE_ADMIN'));
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);
return $client;
}
I do not know what am I doing wrong?
I have to note that my project does the authentication trough x-api-key value in header.
This is a whole new world in Symfony regarding tests for me, so any guidance and help is highly appreciated.
Related
I'm new on Symfony functional test world and trying to test my first API controller with a lot of trouble...
I'm trying to test that endpoint :
$token = $em->getRepository(Token::class)->findOneBy(['value' => $tokenValue]);
if ($token && $this->getCurrentUser() && $token->getUser()->getId() === $this->getUser()->getId()) {
$em->remove($token);
$em->flush();
} else {
throw new NotFoundHttpException();
}
Problems: That controller use EntityManager, TokenInterface (not used, just for example), and a string token value.
I can provide entityManager interface with initializing it in static::$container, but can't with TokenInterface and don't know how to proceed for $tokenValue
Here's my test :
$this->tokenStorage = self::$container->get('security.token_storage');
$this->session = self::$container->get('session');
$this->currentUser = $this->generateFakeEntity(User::class, $currentUserData);
$token = new UsernamePasswordToken($this->currentUser, null, 'api', $this->currentUser->getRoles());
$this->tokenStorage->setToken($token);
$this->session->set('_security_api', serialize($this->tokenStorage->getToken()));
$this->session->save();
$cookie = new Cookie($this->session->getName(), $this->session->getId());
$this->client->getCookieJar()->set($cookie);
$this->client->request(Request::METHOD_DELETE, '/api/external/v1/fr/34pas40EasterEGGDigitemisKeyTesterCDouterCorrigerCAbdiquer2020ItIsNotABugItIsAFeature/34pas40/auth-tokens/1');
$this->assertResponseStatusCodeSame($expectedCode, $this->wrongStatusCodeError($expectedCode, self::PREFIX_SUPER_ADMIN . '/auth-tokens/', __METHOD__));
$tokenValue error :
Controller "App\Api\SuperAdminApiController::removeAuthTokenAction()" requires that you provide a value for the "$tokenValue" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
TokenInterface error :
Symfony\Component\DependencyInjection\Exception\RuntimeException: Cannot autowire argument $token of "App\Api\SuperAdminApiController::removeAuthTokenAction()": it references interface "Symfony\Component\Security\Core\Authentication\Token\TokenInterface" but no such service exists. Did you create a class that implements this interface?
Question: How to proceed to inject that required arguments from a functional test?
Thank you in advance for your time :)
I am running a Symfony 2.8 based web app using FOSUserBundle to manage users. Creating new users with a web form is absolutely no problem.
Now I would like to add a feature to create new users with a REST api. Of course submitting username, password, email, etc. to a controller is no big deal:
public function registerAction(Request $request) {
$requestJson = json_decode($request->getContent(), true);
$username = $requestJson[JSON_KEY_USERNAME];
$email = $requestJson[JSON_KEY_MAIL];
$password = $requestJson[JSON_KEY_PASSWORD];
...
$this->registerUser($username, $email, $password);
...
}
private function registerUser($username, $email, $password, $locale, $timezone, $currency) {
$userManager = $this->get('fos_user.user_manager');
$emailExist = $userManager->findUserByEmail($email);
$userNameExists = $userManager->findUserByUsername($username);
if ($emailExist || $userNameExists)
return false;
$user = $userManager->createUser();
$user->setUsername($username);
$user->setEmail($email);
$user->setPlainPassword($password);
...
$user->setLocked(0);
$user->setEnabled(0);
$userManager->updateUser($user);
return true;
}
However, this performs no validation at all. If for example the username is empty an NotNullConstraintViolationException is thrown when persisting the user object.
Of course I could manually re-implement the same validation process which is used by the RegistrationForm (username not empty, not taken, no invalid characters, password min length, e-mail format, etc.) and pass back the same error messages but this would mean to reinvent the wheel.
Is it somehow possible to run the exact same validation which is used by the RegistrationForm?
Symfony validator can work independently. In a controller you can use validator service like this:
$violations = $this->get('validator')->validate($user, null, ['your_groups_here']);
// Or inject Symfony\Component\Validator\Validator\ValidatorInterface into a service.
It will return a ConstraintViolationListInterface, you can loop trough this object.
You can check FOSUserBundle validation groups here: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/config/validation.xml
So I'm using Lexik JWT bundle (Symfony 2.8) to authenticate over Google and when user is logging in it works well. My Success handler looks like this:
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$jwt = $this->jwtManager->create($user);
$response = new JsonResponse();
$event = new AuthenticationSuccessEvent(['token' => $jwt], $user, $response);
$this->dispatcher->dispatch(Events::AUTHENTICATION_SUCCESS, $event);
$redirectResponse = new RedirectResponse('http://localhost:3000?token='.$event->getData()['token']."&username=".$user->getUsername());
return $redirectResponse;
}
So I'm redirecting user to some localhost and passing token as "token" get variable and that works well. Later I can pass that token value trough header and I get authenticated.
Problem is - I want to get the same token from my controller. I'm using the similar code:
$jwtManager = $this->get('lexik_jwt_authentication.jwt_manager');
$tokenStorage = $this->get('security.token_storage');
$token = $tokenStorage->getToken();
$user = $token->getUser();
$jwt = $jwtManager->create($user);
$response = new JsonResponse();
$event = new AuthenticationSuccessEvent(['token' => $jwt], $user, $response);
$token = $event->getData()['token'];
echo $token;
And I really get some token, but that's not the same one I get from success handler. Tried passing it as header "Autorization" parameter, but it doesn't work. I'm getting 401 error and message:
Unable to verify the given JWT through the given configuration. If the \"lexik_jwt_authentication.encoder\" encryption options have been changed since your last authentication, please renew the token. If the problem persists, verify that the configured keys/passphrase are valid.
What I'm doing wrong here? Why I'm getting different token and how can I get token I'm getting form success handler?
Found the solution. It goes like:
$user = $this->get('security.token_storage')->getToken()->getUser();
$jwtManager = $this->get('lexik_jwt_authentication.jwt_manager');
$token = $jwtManager->create($user);
I know this is an old question, but I found a solution that let you use the token anywhere, not just in the controller.
Instead of using TokenInterface, use TokenStorageInterface
public function __construct(TokenStorageInterface $tokenStorage) {
$this->token = $tokenStorage->getToken();
$this->user = $this->token->getUser();
}
I try to integrate the bundle samlspbundle on a project running with fosuserbundle.
I actually received information from my idp which send me the saml with the email address of the user.
What i'm trying to do is load the user from my table fosuser and then authenticate it.
this is the method i am in my model SamlToUser :
private function loadUserByTargetedID($targetedID)
{
$repository = $this->container->get('doctrine')->getManager()->getRepository('MCCAppBDDBundle:User');
$user = $repository->findOneBy(
array('email' => $targetedID)
);
if ($user) {
$userManager = $this->container->get('fos_user.user_manager');
$url = $this->container->get('router')->generate('homepage');
$response = new RedirectResponse($url);
$this->container->get('fos_user.security.login_manager')->loginUser(
$this->container->getParameter('fos_user.firewall_name'),
$user,
null
);
$userManager->updateUser($user);
return $user;
}
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
}
After that i have this error : PHP Warning: session_regenerate_id(): Cannot regenerate session id - headers already sent
I'm not sure is the right thing to do.
If you need other detail, i can give you.
Thanks to help.
I have made my own custom FB login function because I need a certain flow that I couldn't achieve with HWIOAuthBundle. Everything works great, except one thing... I don't know how to set remember me functionality when I log in a user through my controller.
This is the LOGIN code I've got so far:
public function loginAction() {
// Facebook login code here
...
// User already connected with FB acc. Just log in.
$token = new UsernamePasswordToken($user, null, "main", $user->getRoles());
$this->get("security.context")->setToken($token);
//now dispatch the login event
$request = $this->get("request");
$event = new InteractiveLoginEvent($request, $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
// Redirect user to hime page
return $this->redirect($this->generateUrl('/home'));
}
Thank you for any help or advice in advance!
You can use this remember token not the normal one it should be fixed :
$key = $this->container->getParameter('secret');
$token = new RememberMeToken($user, 'main', $key); //main is your firewall use name you gave to it.
$this->get('security.context')->setToken($token);