I'm trying to logout an user (session) when the Logout link is clicked. It seems to work when I pressed the Logout, but if I change the path, the user still alive and I don't know why.
Here's my Controller:
/**
* #Route("/logout", name="logout")
*/
public function logoutAction(Request $request)
{
$helper = $this->get('security.authentication_utils');
$this->container->get('security.token_storage')->setToken(null);
$this->get('session')->set('id', null);
$request->getSession()->invalidate();
$this->get('security.token_storage')->setToken(null);
return $this->render(
'login.html.twig',
array(
'last_username' => null,
'error' => null,
)
);
}
Here's my security.yml:
security:
# ...
main:
anonymous: ~
guard:
authenticators:
- app.form_login_authenticator
logout:
path: logout
target: logout
invalidate_session: true
Here's the routing.yml:
app:
resource: '#AppBundle/Controller/'
logout:
path: /logout
Remove the logoutAction and check if it works. This action is provided by Symfony itself. You should only set logout path in security.yml what you did.
Related
If I call / and am not logged in, I get the error ERR_TOO_MANY_REDIRECTS.
Actually, I should be redirected to the login page. Where’s the mistake?
security.yaml:
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
logout:
path: app_logout
form_login:
login_path: app_login
check_path: app_login
access_control:
- { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
role_hierarchy:
ROLE_USER: ROLE_USER
ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH]
Controller:
/**
* #Route("/{id}",defaults={"id" = null} , name="app_dashboard")
*/
public function index(PositiveTimeRepository $positiveTimeRepository, $id): Response
{
return $this->render('dashboard/index.html.twig', [
'positiveData' => $positiveTimeRepository->findBy([
'user' => $this->getUser()]),
'issetGetID' => $id
]);
}
SecurityController:
/**
* #Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('login/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error
]);
}
I updated my controllers and security.yaml
You don’t have to check for the roles in your controller yourself the security package does that with the firewall you defined, but you would have to define your login url:
firewalls:
main:
# ...
form_login:
# "login" is the name of the route created previously
login_path: login
check_path: login
Here is my problem, im working with FOSUserBundle and Symfony 3, i've created a UserBundle that inherits FOSUserBundle so i can override some of it's parts when needed, for project's needs i've created two firewalls, one for the backend and the second for the frontend part, everything worked fine until i tried to edit the current logged user's profile(username and email), when i use var_dump($this->getUser()) while loading the editing form i get all of the current user's data but after submiting i get var_dump($this->getUser()) = null and get redirected to login form.
This is the security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
admin:
pattern: /admin(.*)
form_login:
provider: fos_userbundle
login_path: /admin/login
check_path: /admin/login_check
csrf_token_generator: security.csrf.token_manager
default_target_path: /admin/
always_use_default_target_path: true
logout:
path: /admin/logout
target: /admin/login
anonymous: true
main:
pattern: ^/
form_login:
provider: fos_userbundle
login_path: /login
check_path: /login_check
csrf_token_generator: security.csrf.token_manager
default_target_path: /
always_use_default_target_path: true
# if you are using Symfony < 2.8, use the following config instead:
# csrf_provider: form.csrf_provider
logout:
path: /logout
target: /login
anonymous: true
access_control:
- { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
The routes for the admin(backend) part in routing.yml:
admin_login:
path: /admin/login
defaults: { _controller: FOSUserBundle:Security:login }
admin_check:
path: /admin/login_check
defaults: { _controller: FOSUserBundle:Security:check }
admin_logout:
path: /admin/logout
defaults: { _controller: FOSUserBundle:Security:logout }
admin_profile_show:
path: /admin/profile/show
defaults: { _controller: FOSUserBundle:Profile:show }
admin_profile_edit:
path: /admin/profile/edit
defaults: { _controller: FOSUserBundle:Profile:edit }
admin_profile_change_password:
path: /admin/profile/changePassword
defaults: { _controller: FOSUserBundle:ChangePassword:changePassword }
And this is the editAction in my ProfileController.php that can be accessed via the route admin_profile_edit in the routing file above, that action overrides the one in FOSUserBundle
// UserBundle/Controller/ProfileController.php
public function editAction(Request $request)
{
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
/** #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');
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
$requestAttributes = $this->container->get('request_stack')->getCurrentrequest()->attributes;
if ($requestAttributes->get('_route') == 'admin_profile_edit') {
$template = sprintf('UserBundle:Profile:user_admin_edit.html.twig');
} else {
$template = sprintf('FOSUserBundle:Profile:edit.html.twig');
}
return $this->render($template, array(
'form' => $form->createView()
));
}
Now when loading the editing form everything works fine and the current user data show perfectly if i use var_dump($this->getUser()), but after submiting the form the same var_dump is null and im redirected to the login form.
I don't know if im missing somthing or doing it wrong, because i've already did the same thing with another project in Symfony 2.8 and it worked perfectly without any problems.
I hope that i've provided the max informations for my problem.
Thank you for your help.
Extend base Action(login), and add custom logic with redirect;
See Link:
This redirects using a 301 return code to the client:
$this->redirect(
$this->generateUrl(
'person_in_need_view',
array('id' => $id)
)
);
This redirects internally:
$this->forward(
'person_in_need_view',
array('id' => $id)
)
The solution is to change the form action from fos_user_profile_edit to admin_profile_edit, the route that loaded the edit form was admin_profile_edit for the backend users, but i've forgot to change it in my form action that's why after submitting the user wasn't reconized anymore because it was redirected to the fos_user_profile_edit editAction that is supposed to work in the frontend part.
I'm trying to do functional test for the routes that are behind the firewall. I'm not sure what I'm doing wrong but the test for the route admin/dashboard fails. Any ideas?
<?php
namespace AppBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class ApplicationAvailabilityFunctionalTest extends WebTestCase
{
private $client;
public function setUp()
{
$this->client = self::createClient();
}
/**
* #dataProvider urlProvider
*/
public function testPageIsSuccessful($url)
{
$this->client->request('GET', $url);
$this->assertTrue($this->client->getResponse()->isSuccessful());
}
public function urlProvider()
{
$this->logIn();
return array(
array('/'),
array('/admin/login'),
array('/admin/dashboard'),
);
}
public function logIn()
{
$this->client = self::createClient();
$session = $this->client->getContainer()->get('session');
$firewall = 'our_db_provider';
$token = new UsernamePasswordToken('admin', 'admin', $firewall, array('ROLE_ADMIN'));
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
}
//UPDATE
Here's the error I get
1) AppBundle\Tests\ApplicationAvailabilityFunctionalTest::testPageIsSuccessful with data set #2 ('/admin/dashboard')
Failed asserting that false is true.
/Users/me/Projects/cms/src/AppBundle/Tests/ApplicationAvailabilityFunctionalTest.php:27
//UPDATE 2
Here's the dump of $token variable
Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken {#488
-credentials: null
-providerKey: "security"
-user: "admin"
-roles: array:1 [
0 => Symfony\Component\Security\Core\Role\Role {#487
-role: "ROLE_ADMIN"
}
]
-authenticated: true
-attributes: []
}
//UPDATE 3
`security:
encoders:
AppBundle\Entity\Admin\User:
algorithm: bcrypt
providers:
our_db_provider:
entity:
class: AppBundle\Entity\Admin\User
property: username
access_control:
- { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, roles: ROLE_ADMIN }
firewalls:
default:
anonymous: ~
http_basic: ~
form_login:
login_path: /admin/login
check_path: /admin/login_check
csrf_provider: security.csrf.token_manager
logout:
path: /admin/logout
target: /admin/login
provider: our_db_provider
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~`
The route is not public
The failing test is on the /admin/dashboard route that probably is protected by authentication so the server response is not successfully (200 OK) but (403 access denied or 302 redirect)
So you must test your route differently: the route is protected so check for 403 or that redirect to login page
Check the doc about How to Simulate Authentication with a Token in a Functional Test
And test that an authenticated user see correctly the page
I have an action addFavorite witch only AUTHENTICATED user can access . When user is anonymous, he will be redirected to login form. So I would like the user to be redirected to this action after connection.
Right now he is redirected to default path
security.yml
main:
pattern: ^/
anonymous: true
provider: main
form_login:
login_path: fos_user_security_login
check_path: fos_user_security_check
always_use_default_target_path: false
default_target_path: /
target_path_parameter: _target_path
use_referer: true
csrf_provider: form.csrf_provider
logout:
path: fos_user_security_logout
target: /
remember_me:
key: %secret%
actionController:
public function addFavoriteAction(Travel $travel, Request $request)
{
if (!$this->get('security.context')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return $this->redirect($this->generateUrl('fos_user_security_login'));
}
if ($request->getMethod() == 'GET') {
$em = $this->getDoctrine()->getManager();
$user = $this->get('security.context')->getToken()->getUser();
$nbFav = $em->getRepository('ProjectTravelBundle:Favorite')->findOneBy(array('user' =>$user, 'travel' =>$travel));
if(!$nbFav)
{
$favorite = new Favorite();
$favorite->setUser($user);
$favorite->setTravel($travel);
$em->persist($favorite);
$em->flush();
}
}
$referer = $this->getRequest()->headers->get('referer');
return $this->redirect($referer);
}
According to Symfony's documentation, the user is automatically redirected to the page they were trying to access. So if they were on the addFavorite route already while not authenticated, they would be logged in and sent right back to it.
You can also set it manually in the form:
<input type="hidden" name="_target_path" value="your_route_name_goes_here" />
I am trying to configure remember me feature of symfony using their default mechanism as described here; but couldn't make it work.
Cookie named REMEMBERME is created, but is set to deleted and its expire date is 1970. This is why I suppose remember me function is not working. However, when I use (always_remember_me: true) in security.yml, the code works nicely but it doesn't suite my purpose. Using (always_remember_me: true) even if user doesn't check REMEMBER ME checkbox in UI the cookie gets created.
Any help is highly appreciated
I am using the version 2.3.7
This is my complete security.yml file:
security:
firewalls:
main:
pattern: ^/
anonymous: ~
remember_me:
key: "%secret%"
lifetime: 31536000 # a year
domain: ~
path: /
remember_me_parameter: _remember_me
#always_remember_me: true
form_login:
login_path: _my_login
check_path: _my_login_check
always_use_default_target_path: true
default_target_path: /out/homepage
remember_me: true
logout:
path: _my_logout
target: _my_login
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: IS_AUTHENTICATED_REMEMBERED }
providers:
chain_provider:
chain:
providers: [in_memory, user_db]
in_memory:
memory:
users:
user: { password: user, roles: 'ROLE_USER' }
admin: { password: admin, roles: ['ROLE_ADMIN', 'ROLE_USER'] }
user_db:
entity: {class: ProjectName\StoreBundle\Entity\User, property: username}
encoders:
ProjectName\StoreBundle\Entity\User:
algorithm: sha1
iterations: 1
encode_as_base64: false
Symfony\Component\Security\Core\User\User: plaintext
LoginController.php
class LoginController extends Controller
{
/**
* #Route("/login", name="_my_login")
* #Template()
*/
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContext::AUTHENTICATION_ERROR
);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
);
}
/**
* #Route("/login_check", name="_my_login_check")
*/
public function loginCheckAction()
{
}
I added the value attribute inside the input tag. After removing it, it worked for now. :)
<input type="checkbox" id="remember_me" name="_remember_me" checked /> Remember me
I suppose the checkbox is named incorrectly. It should have the 'name' attribute set to '_remember_me' to make the magic happen. Can you post the content of the form template?