Rendering multiple forms on same page with Symfony 3 - php

I'd like to show a login and a registration form on the same page. However it seems that we cannot render the same template with different form variables in different two actions in a controller.
Here is what I mean;
class SecurityController extends Controller {
/**
* #Route("/", name="login")
*/
public function loginAction(Request $request)
{
//Check if the user is already authenticated
if($this->container->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
return $this->redirect($this->generateUrl('dashboard'));
}
$authenticationUtils = $this->get('security.authentication_utils');
// get the error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
$this->addFlash(
'welcome',
'Welcome back!'
);
return $this->render(
'::landing.html.twig',
array(
// last username entered by the user
'last_username' => $lastUsername,
'error' => $error,
)
);
$registrationform = $this->get('form.factory')->createNamedBuilder('registrationform', UserType::class);
}
/**
* #Route("/register", name="register")
*/
public function registerAction(Request $request)
{
//Check authentication
if($this->container->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
return $this->redirect($this->generateUrl('dashboard'));
}
// get the authentication utils
$authenticationUtils = $this->get('security.authentication_utils');
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
// build the form
$user = new User();
$form = $this->createForm(UserType::class, $user);
// handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->setRoles(array('ROLE_USER'));
// Encode the password (you could also do this via Doctrine listener)
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
// save the User!
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
$this->get('session')->set('_security_main', serialize($token));
// ... do any other work - like sending them an email, etc
// maybe set a "flash" success message for the user
$this->addFlash(
'success',
'Please complete your profile now.'
);
$message = \Swift_Message::newInstance()
->setSubject('You have successfully signed up!')
->setFrom('no-reply#kampweb.com')
->setTo($user->getUsername())
->setBody($this->renderView(
':emails:registration.html.twig'),'text/html');
$this->get('mailer')->send($message);
return $this->redirectToRoute('update');
} else{
$errors = $this->get('validator')->validate( $user );
}
return $this->render(
'::landing.html.twig',
array( 'form' => $form->createView(),
'last_username' => $lastUsername,
'error' => $error,
)
);
}
}
Above I have my login and registration action. I'd like to pass the 'form' variable to the same twig template that I render with login action. When I try to do that I'm getting the following exception.
Variable "form" does not exist in ::landing.html.twig at line 50
500 Internal Server Error - Twig_Error_Runtime

I would do it like this
2 twig files -
login.html.twig and register.html.twig - Every file render by himself the action
now the third twig file called baseLogin.html.twig
in this file, I render the two files (login and register) by calling controller
For example
baseLogin.html.twig
<div>
{{ render(controller('UserBundle:Security:login')) }}
</div>
<div>
{{ render(controller('UserBundle:Registration:register')) }}
</div>

Related

Symfony 3, populating token and refreshing user

repository with issue
I have a form for entity User with email field:
->add('email', EmailType::class, [
'constraints' => [
new NotBlank(),
new Email([
'checkMX' => true,
])
],
'required' => true
])
when i'm editing email to something like test#gmail.com1 and submit form it shows me error "This value is not a valid email address." THat's ok, but after that symfony populate wrong email into token and when i'm going to any other page or just reload page, i'm getting this:
WARNING security Username could not be found in the selected user
provider.
i think question is: why symfony populate wrong Email that failed validation into token and how i could prevent it?
controller:
public function meSettingsAction(Request $request)
{
$user = $this->getUser();
$userUnSubscribed = $this->getDoctrine()->getRepository('AppBundle:UserUnsubs')->findOneBy(
[
'email' => $user->getEmail(),
]
);
$form = $this->createForm(UserSettingsType::class, $user);
$form->get('subscribed')->setData(!(bool)$userUnSubscribed);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/**
* #var $user User
*/
$user = $form->getData();
/** #var UploadedFile $avatar */
$avatar = $request->files->get('user_settings')['photo'];
$em = $this->getDoctrine()->getManager();
if ($avatar) {
$avatar_content = file_get_contents($avatar->getRealPath());
$avatarName = uniqid().'.jpg';
$oldAvatar = $user->getPhoto();
$user
->setState(User::PHOTO_STATE_UNCHECKED)
->setPhoto($avatarName);
$gearmanClient = $this->get('gearman.client');
$gearmanClient->doBackgroundDependsOnEnv(
'avatar_content_upload',
serialize(['content' => $avatar_content, 'avatarName' => $avatarName, 'oldAvatar' => $oldAvatar])
);
}
$subscribed = $form->get('subscribed')->getData();
if ((bool)$userUnSubscribed && $subscribed) {
$em->remove($userUnSubscribed);
} elseif (!(bool)$userUnSubscribed && !$subscribed) {
$userUnSubscribed = new UserUnsubs();
$userUnSubscribed->setEmail($form->get('email')->getData())->setTs(time());
$em->persist($userUnSubscribed);
}
$user->setLastTs(time());
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->get('user.manager')->refresh($user);
return $this->redirectToRoute('me');
}
return $this->render(
':user:settings.html.twig',
[
'form' => $form->createView(),
]
);
}
UPD:
it works fine if i change in OAuthProvider:
/**
* #param \Symfony\Component\Security\Core\User\UserInterface $user
*
* #return \Symfony\Component\Security\Core\User\UserInterface
*/
public function refreshUser(UserInterface $user)
{
return $this->loadUserByUsername($user->getName());
}
to:
/**
* #param \Symfony\Component\Security\Core\User\UserInterface $user
*
* #return \Symfony\Component\Security\Core\User\UserInterface
*/
public function refreshUser(UserInterface $user)
{
return $this->userManager($user->getId());
}
but it seems to be dirty hack.
Thanks.
Your user token seems to be updated by the form, even if the email constraint stop the flush.
Can you check if your form past the isValid function ?
You can maybe try to avoid it with an event listener or a validator.
With an event SUBMIT you should be able to check the email integrity, and then add a FormError to avoid the refreshUser.
This is a tricky one, thanks to the repository it was easier to isolate the problem. You are binding the user object form the authentication token to the createForm() method. After the
$form->handleRequest($request)
call the email off the token user object is updated.
I first thought to solve this by implementing the EquatableInterface.html in the User entity but this did not work, as the compared object already had the wrong email address set.
It may also be useful to implement the EquatableInterface interface, which defines a method to check if the user is equal to the current user. This interface requires an isEqualTo() method.)
Than I thought about forcing a reload of the user from the db and resetting the security token, but in the it came to my mind, that it might be sufficient to just refresh the current user object from the database in case the form fails:
$this->get('doctrine')->getManager()->refresh($this->getUser());`
In your controller, this would solve your issue.
/**
* #Route("/edit_me", name="edit")
* #Security("has_role('ROLE_USER')")
*/
public function editMyselfAction(Request $request) {
$form = $this->createForm(User::class, $this->getUser());
if ($request->isMethod(Request::METHOD_POST)) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
} else {
$this->get('doctrine')->getManager()->refresh($this->getUser());
}
}
return $this->render(':security:edit.html.twig',['form' => $form->createView()]);
}
Alternative solution
The issue at the Symfony repository resulted in some valuable input about Avoiding Entities in Forms and
Decoupling Your Security User which provides a more complex approach for a solution to your problem.

inject user data in Symfony, FOS loginAction

I'm writing a Symfony 3.0 application and I have the following situation:
The system provides different login and register pages (one for each subsite)
A user can have the same username to login on different subsites but accounts are managed separately.
My idea was to save the subsite domain's unique name in the username field with # notation, example:
User a.ndrew can access both domain1 and domain2 with the same username but the system stores 2 different accounts with usernames a.ndrew#domain1 and a.ndrew#domain2.
I handle this automatically by overwriting RegistrationController actions and it works fine during registration but now i'm trying to automatically push "#domain" data in the username at login with no luck. I'm overwriting loginAction but I don't understand how to push data in the username.
EDIT (tnx #malcom)
Here is my loginAction called companyUserLoginAction it simply checks if companyName exists and redirect to FOS loginAction, I'd like to be able to add #companyName at the end of the username provided by user in the login form but I don't understand where this information is handled by the controller:
`
/**
* #Route("/{companyName}/login")
*/
public function companyUserLoginAction(Request $request, $companyName)
{
$em = $this->getDoctrine()->getManager();
$company = $em->getRepository('CompanyBundle:Company')->findOneBy(array('name' => $companyName));
if ($company == null)
{
throw new NotFoundHttpException();
}
return $this->loginAction($request);
}
/**
*Default FOS loginAction
*/
public function loginAction(Request $request)
{
/** #var $session \Symfony\Component\HttpFoundation\Session\Session */
$session = $request->getSession();
if (class_exists('\Symfony\Component\Security\Core\Security')) {
$authErrorKey = Security::AUTHENTICATION_ERROR;
$lastUsernameKey = Security::LAST_USERNAME;
} else {
// BC for SF < 2.6
$authErrorKey = SecurityContextInterface::AUTHENTICATION_ERROR;
$lastUsernameKey = SecurityContextInterface::LAST_USERNAME;
}
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
// last username entered by the user
$lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);
if ($this->has('security.csrf.token_manager')) {
$csrfToken = $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue();
} else {
// BC for SF < 2.4
$csrfToken = $this->has('form.csrf_provider')
? $this->get('form.csrf_provider')->generateCsrfToken('authenticate')
: null;
}
return $this->renderLogin(array(
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
));
}
}
`

FOSUserBundle edit user not working properly

I'm using Symfony2 with FOSUserBundle, the problem I have encountered was in editing a user account, when I tried to edit a specific user account, the retrieved user info is correct but when I tried to update the retrieved user info, the currently logged account would be the one being edited not the retrieved user account, how is it possible? What's wrong with my code?
ProfileController :
//edit user
public function editUserAction($id, Request $request)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('MatrixUserBundle:User')->find($id);
if (!is_object($user)) {
throw new AccessDeniedException('This user does not have access to this section.');
}
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.profile.form.factory');
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
$userManager->updateUser($user);
$session = $this->getRequest()->getSession();
$session->getFlashBag()->add('message', 'Successfully updated');
$url = $this->generateUrl('matrix_edi_viewUser');
$response = new RedirectResponse($url);
}
return $this->render('FOSUserBundle:Profile:edit.html.twig', array(
'form' => $form->createView()
));
}
After the given user is correctly updated, you are returning a redirection to a custom route.
I guess the route matrix_edi_viewUser is an override of the original FOSUB\ProfileController::showAction.
Also, you have to create a specific showUserAction and pass it the updated user as argument.
The following code should works :
public function showUserAction($id)
{
$user = $em->getDoctrine()
->getManager()
->getRepository('MatrixUserBundle:User')
->find($id);
if (!is_object($user)) {
throw new AccessDeniedException('This user does not have access to this section.');
}
return $this->render('FOSUserBundle:Profile:show.html.twig', array('user' => $user));
}
The route :
matrix_edi_viewUser:
path: /users/{id}/show
defaults: { _controller: MatrixUserBundle:Profile:showUser }
And in your editAction, change your redirection to :
$url = $this->generateUrl('matrix_edi_viewUser', array('id' => $user->getId());
$response = new RedirectResponse($url);
Now, the user informations displayed in show will be the informations of the updated user.

Laravel Routing returns blank page

to make it a bit short. I just made a registration form fully working with a controller, the routes and the view. Now I know it's common sense to use a Model for it and in the controller only call the method in the model. So i thought okay lets fix that. Now when I register an account I get a blank page. I bet the redirect is going wrong but I can't fix it maybe you can?
RegisterController.php
public function doRegister(){
$user = new User();
$user->doRegister();
}
User.php (model)
public function doRegister()
{
// process the form here
// create the validation rules ------------------------
$rules = array(
'username' => 'required|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|min:5',
'serial_key' => 'required|exists:serial_keys,serial_key|unique:users'
);
// create custom validation messages ------------------
$messages = array(
'required' => 'The :attribute is important.',
'email' => 'The :attribute is not a legit e-mail.',
'unique' => 'The :attribute is already taken!'
);
// do the validation ----------------------------------
// validate against the inputs from our form
$validator = Validator::make(Input::all(), $rules);
// check if the validator failed -----------------------
if ($validator->fails()) {
// get the error messages from the validator
$messages = $validator->messages();
// redirect our user back to the form with the errors from the validator
return Redirect::to('/register')
->withErrors($validator)
->withInput(Input::except('password', 'password_confirm'));
} else {
// validation successful ---------------------------
// our duck has passed all tests!
// let him enter the database
// create the data for our duck
$duck = new User;
$duck->username = Input::get('username');
$duck->email = Input::get('email');
$duck->password = Hash::make(Input::get('password'));
$duck->serial_key = Input::get('serial_key');
// save our user
$duck->save();
// redirect with username ----------------------------------------
return Redirect::to('/registered')->withInput(Input::old('username'));
}
}
you need to make $user->doRegister(); a return statement
in your RegisterController you have to do
public function doRegister(){
$user = new User();
return $user->doRegister();
}
try this
return Redirect::to('/registered')
->with('bill_no', Input::get('username'));
in the '/registered' controller,..
use this
$username = Session::get("username");
above worked for me,...

Issues building login authenticator for symfony2

I'm trying to build a login service that sets an authentication token as a cookie sends it to the user in the response. Though it doesn't seem like I'm able to authenticate the user. Is there something I'm missing in my code?
public function login(Response $response,User $user, $roles)
{
// The name of my firewall is 'secured_area'
$firewall = 'secured_area';
// Build usernamepasswordtoken. I'm not really certain what to set the credentials
// but in all the examples I've seen this field set to null. roles is an array of
// of string names for roles. I'm simply passing in array('ROLE_USER') into $roles.
// The $user is actually a user entity that should get serialized with the token.
$token = new UsernamePasswordToken($user, null, $firewall, $roles);
// set the session string name and serialize my token.
$this->session->set('_security_'.$firewall, serialize($token));
$this->session->save();
// make new cookie and send it off to the client
$cookie = new Cookie($this->session->getName(), $this->session->getId());
$response->headers->setCookie($cookie);
return $response;
}
// In my controller I simply do
return login(new Response("work"), $user, array('ROLE_USER'));
Is there something blatantly wrong with my work?
Examlpe:
public function loginAction()
{
$request = $this->getRequest();
$csrfToken = $this->container->has('form.csrf_provider')
? $this->container->get('form.csrf_provider')->generateCsrfToken('authenticate')
: null;
if ($this->get('security.context')->isGranted('ROLE_USER'))
{
return $this->redirect($this->generateUrl('homepage'));
}
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$session = $request->getSession();
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
if ($error) {
$error = $error->getMessage();
}
$lastUsername = (null === $session) ? '' : $request->getSession()->get(SecurityContext::LAST_USERNAME);
$form = $this->createForm('form_login')->createView();
return $this->render('AcmeTestBundle:User:login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
'form' => $form
));
}

Categories