How to set session variables for all the controllers in Symfony2? - symfony

How do I create and access Symfony 2 session variables in my controllers.
I used like this.
$session = new Session();
$session->start();
$session->set('loginUserId',$user['user_id']);
I want to know how to use the above session variable in all my controllers to access.

One way of using Sessions in Symfony in controller is:
setting:
$this->get('session')->set('loginUserId', $user['user_id']);
getting:
$this->get('session')->get('loginUserId');
If you use standard framework edition

From the docs:
Symfony sessions are designed to replace several native PHP functions.
Applications should avoid using session_start(),
session_regenerate_id(), session_id(), session_name(), and
session_destroy() and instead use the APIs in the following section.
and:
While it is recommended to explicitly start a session, a sessions will
actually start on demand, that is, if any session request is made to
read/write session data.
So sessions is started automatically and can be accessed e.g. from controllers via:
public function indexAction(Request $request)
{
$session = $request->getSession();
...
}
or:
public function indexAction()
{
$session = $this->getRequest()->getSession();
// or
$session = $this->get('session');
...
}
than:
// store an attribute for reuse during a later user request
$session->set('foo', 'bar');
// get the attribute set by another controller in another request
$foobar = $session->get('foobar');
// use a default value if the attribute doesn't exist
$filters = $session->get('filters', array());

http://symfony.com/doc/current/components/http_foundation/sessions.html
use Symfony\Component\HttpFoundation\Session\Session;
$session = new Session();
$session->start();
// set and get session attributes
$session->set('name', 'Drak');
$session->get('name');
// set flash messages
$session->getFlashBag()->add('notice', 'Profile updated');
// retrieve messages
foreach ($session->getFlashBag()->get('notice', array()) as $message) {
echo '<div class="flash-notice">'.$message.'</div>';
}

Related

How to create a new FOSUserBundle user programatically using the same validation as on web form?

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

fosuserbundle ldap configuration for strange use case

I'm trying to create a fosuserbundle for a quite strange use case, which is mandatory requirement, so no space to diplomacy.
Use case is as follow:
users in a mongo db table populated by jms messages -no registration form
users log in by ldap
user record not created by ldap, after a successful login username is checked against mongodb document
Considering that ldap could successfully log in people that exhist in ldap but cannot access site (but login is still successful), what could be the best way to perform such authentication chain?
I was thinking about some possible options:
listen on interactive login event, but imho there's no way to modify an onSuccess event
create a custom AuthenticationListener to do another check inside onSuccess method
chain authentication using scheb two-factor bundle
any hint?
I've used Fr3DLdapBundle which can be incorporate with FOSUserBundle quite easily (I'm using the 2.0.x version, I have no idea if the previous ones will do the same or be as easy to set up).
In the LdapManager (by default) it creates a new user if one is not already on the database which is not what I wanted (and doesn't seem to be what you want) so I have added my own manager that checks for the presence of the user in the database and then deals with the accordingly.
use FR3D\LdapBundle\Ldap\LdapManager as BaseLdapManager;
.. Other use stuff ..
class LdapManager extends BaseLdapManager
{
protected $userRepository;
protected $usernameCanonicalizer;
public function __construct(
LdapDriverInterface $driver,
$userManager,
array $params,
ObjectRepository $userRepository,
CanonicalizerInterface $usernameCanonicalizer
) {
parent::__construct($driver, $userManager, $params);
$this->userRepository = $userRepository;
$this->usernameCanonicalizer = $usernameCanonicalizer;
}
/**
* {#inheritDoc}
*/
public function findUserBy(array $criteria)
{
$filter = $this->buildFilter($criteria);
$entries = $this->driver->search(
$this->params['baseDn'], $filter, $this->ldapAttributes
);
if ($entries['count'] > 1) {
throw new \Exception('This search can only return a single user');
}
if ($entries['count'] == 0) {
return false;
}
$uid = $entries[0]['uid'][0];
$usernameCanonical = $this->usernameCanonicalizer->canonicalize($uid);
$user = $this->userRepository->findOneBy(
array('usernameCanonical' => $usernameCanonical)
);
if (null === $user) {
throw new \Exception('Your account has yet to be set up. See Admin.');
}
return $user;
}

Symfony2 - Redirect response from request EventListener in dev mode while ignoring built in request events

I am building my own user management system in Symfony2 (not using FOSUserBundle) and want to be able to force users to change their password.
I have setup an EventListener to listen to the kernal.request event, then I perform some logic in the listener to determine if the user needs to change their password; if they do, then they are redirected to a "Change Password" route.
I add the service to my config.yml to listen on the kernal.request:
password_change_listener:
class: Acme\AdminBundle\EventListener\PasswordChangeListener
arguments: [ #service_container ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onMustChangepasswordEvent }
And then the listener:
public function onMustChangepasswordEvent(GetResponseEvent $event) {
$securityContext = $this->container->get('security.context');
// if not logged in, no need to change password
if ( !$securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED') )
return;
// If already on the change_password page, no need to change password
$changePasswordRoute = 'change_password';
$_route = $event->getRequest()->get('_route');
if ($changePasswordRoute == $_route)
return;
// Check the user object to see if user needs to change password
$user = $this->getUser();
if (!$user->getMustChangePassword())
return;
// If still here, redirect to the change password page
$url = $this->container->get('router')->generate($changePasswordRoute);
$response = new RedirectResponse($url);
$event->setResponse($response);
}
The problem I am having is that in dev mode, my listener is also redirecting the profiler bar and assetic request events. It works when I dump assets and clear cache and view the site in production mode.
Is there a way I can ignore the events from assetic/profiler bar/any other internal controllers? Or a better way to redirect a user to the change_password page (not only on login success)?
Going crazy thinking up wild hack solutions, but surely there is a way to handle this elegantly in Symfony2?
This is the very hack solution I am using for now:
Determine if in dev environment
If so, get an array of all the routes
Filter the route array so that only the routes I have added remain
Compare the current route to the array of routes
If a match is found, this means that the event is not an in-built controller, but must be one that I have added, so perform the redirect.
And this is the madness that makes that work:
// determine if in dev environment
if (($this->container->getParameter('kernel.environment') == 'dev'))
{
// Get array of all routes that are not built in
// (i.e You have added them yourself in a routing.yml file).
// Then get the current route, and check if it exists in the array
$myAppName = 'Acme';
$routes = $this->getAllNonInternalRoutes($myAppName);
$currentRoute = $event->getRequest()->get('_route');
if(!in_array($currentRoute, $routes))
return;
}
// If still here, success, you have ignored the assetic and
// web profiler actions, and any other actions that you did not add
// yourself in a routing.yml file! Go ahead and redirect!
$url = $this->container->get('router')->generate('change_password_route');
$response = new RedirectResponse($url);
$event->setResponse($response);
And the crazy hack function getAllNonInternalRoutes() that makes it work (which is a modification of code I found here by Qoop:
private function getAllNonInternalRoutes($app_name) {
$router = $this->container->get('router');
$collection = $router->getRouteCollection();
$allRoutes = $collection->all();
$routes = array();
foreach ($allRoutes as $route => $params)
{
$defaults = $params->getDefaults();
if (isset($defaults['_controller']))
{
$controllerAction = explode(':', $defaults['_controller']);
$controller = $controllerAction[0];
if ((strpos($controller, $app_name) === 0))
$routes[]= $route;
}
}
return $routes;
}

how to get the session variable in the view in symfony2

Thanks for your valuable suggestions
i have created a login system where i want to store the id's of users in session variables
this is my controller for login system
use Symfony\Component\HttpFoundation\Session\Session;
class successController extends Controller
{
public function successAction(Request $request)
{
--some code for form--
$repository = $em->getRepository('RepairStoreBundle:users');
$query = $repository->auth($name,$password);
$error="sorry invalid username or password";
if($query== false)
{
return $this->render('RepairLoginBundle:login:login.html.php', array(
'form' => $form->createView(),'error'=>$error,));
}
else
{
$role=$query[0]['role'];
$id=$query[0]['id'];
if($role == 1)
{
$session = new Session();
$session->start();
$session->set('id',$id);
$result=$repository->display();
return $this->render('RepairLoginBundle:login:success.html.php',array('result'=>$result,));
}
else
{
$session = new Session();
$session->start();
$session->set('id',$id);
$res= $repository->edit($id);
return $this->render('RepairLoginBundle:login:user.html.php',array('res'=>$res));
}
}
}
}
when admin logins with role=1 it will render to success.html.php
in this view how can i get the session variable which i have set in the controller.
i have used $session->get('id');
it is giving me server error please help with this
Upfront Authentication should better be done with the Security Component in Symfony2.
Read more about it in The Book - Security. You should probably also take a look at FOSUserBundle
Accessing the session from a PHP template in symfony2:
echo $app->getSession()->get('whatever');
Session Handling
There is an article in the official documentation:
Components/HttpFoundation - Session Data Management
The API documentation for the Session Component can be found here:
http://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Session.html
In the symfony2 standard-edition you can get the session from within a controller with:
$session = $this->getRequest()->getSession();
As you already have the request as an argument in successAction you could access the session with:
$session = $request->getSession();
Set a value with ( $value needs to be serializable ):
$session->set('key',$value);
Get a value with:
$session->get('key');
Saving (and closing) the session can be done with:
$session->save();
You should also loook at the SessionBag class.
you create a SessionBag and register it with the session. see:
Symfony API
In the registered SessionBag - which implements AttributeBagInterface - you can get and set your key/value's as desired.
TIP: If you want to get the current User and you have a container aware controller ( container injected )
you can use:
$user = $this->container->get('security.context')->getToken()->getUser();
if you are extending Symfony's Controller class in the standard-edition - the shorter way is:
$user = $this->get('security.context')->getToken()->getUser();
or even shorter (Symfony > 2.1.x):
$user = $this->getUser();
Alternative ( If your controller is not container aware ):
Define the controller as a service and inject #security.context:
YAML:
# src/Vendor/YourBundle/Resources/config/services.yml
services:
my.controller.service:
class: Vendor\YourBundle\Controller\successController
arguments: ["#security.context"]
Vendor\YourBundle\Controller\successController:
protected $securityContext;
public function __construct(SecurityContextInterface $securityContext)
{
$this->securityContext = $securityContext;
}
then in your action:
$user = $this->securityContext->getToken()->getUser();
Note:: you have to use the service in your routing aswell if you choose the controller-as-service variant. example routing.yml :
[...]
route_name:
pattern: /success
defaults: { _controller: my.controller.service:successAction }
[...]
[...]
Note... you can also inject the session with "#session"
# src/Vendor/YourBundle/Resources/config/services.yml
[...]
arguments: ["#security.context","#session"]
Note injecting the whole container is resource-heavy. advanced developers inject their needed services one-by-one and not the whole container.
Tip: Normally Controller classes are written with a capital first letter - example: *S*uccessController
General TIP: You have unnecessary dublicate code in your example:
// 'if' and 'else' execute the same stuff here
// result: dublicate code = more code = harder to read
if($role == 1)
{
$session = new Session();
$session->start();
[...]
}
else
{
$session = new Session();
$session->start();
[...]
}
should better be ...
// better: put this stuff before the if/else statement
$session = new Session();
$session->start();
if($role == 1)
{
[...]
}
else
{
[...]
}

Correct way to retrieve mails by IMAP in symfony2

I have to develop simple mail client in symfony2 using IMAP. Im wondering what is best way to retrieve messages from server (lets take a gmail as example)?
I did something like this:
public function indexAction($name)
{
$user = 'adress#gmail.com';
$password = 'password';
$mailbox = "{imap.gmail.com:993/imap/ssl}INBOX";
$mbx = imap_open($mailbox , $user , $password);
$ck = imap_check($mbx);
$mails = imap_fetch_overview($mbx,"1:5");
return $this->render('HtstMailBundle:Mail:index.html.twig',array('name'=>$name,'mail'=>$mails));
}
is this right way, or not? It works, but is it compatible with symfony "standards"?
This has nothing to do with symfony "standards". But you can make your code more OOP if you move all login to a service class and use symfony DepencyInjection to create and get your service:
public function indexAction($name)
{
$user = 'adress#gmail.com';
$password = 'password';
$mailbox = "{imap.gmail.com:993/imap/ssl}INBOX";
$mails = $this->get("mail.checker")->receive($user, $password, $mailbox);
return $this->render('HtstMailBundle:Mail:index.html.twig',array('name'=>$name,'mail'=>$mails));
}
Class declaration:
class MailChecker
{
public function receive($user, $password, $mailbox)
{
...imap_check()...
}
}
service declartion:
services:
mail.checker:
class: Project\YourBundle\Service\MailChecker
You can also use this Symfony bundle for that and use it as a service. I is designed for old Symfony2 but tested it with Symfony 3 and works :)

Resources