I am trying to implement the LexikJwt package for my new API:
https://github.com/lexik/LexikJWTAuthenticationBundle
I just added my custom command to create a proper JWT token including my default roles for local testing. The next step would be to actually protect my routes. What i can't find however is exactly on how to do that. This is my current controller implementation using annotation routes
/**
* #Route(
* "/search",
* name="search",
* methods={"GET"},
* )
*/
class Search
{
/**
* #param AddressService $addressService
* #param Request $request
* #return JsonApiResponse
* #throws Exception
*/
public function __invoke(AddressService $addressService, Request $request)
{
$address = $addressService->createFromParams($request->query->all());
try {
$addressCollection = $addressService->search($address);
} catch (AddressNotFoundException $e) {
$addressCollection = [];
}
return new JsonApiResponse($addressCollection);
}
}
However the docs do not say anything about annotation routes and only on yml configs on security firewalls. The main thing i need is the token to be verified:
Is the token valid (matching the public key)
Is the token not expired
Does the route match the given roles
For example, i want the code above, which is an address service to match only if the token matches above and the token holds the role or scope: address:search.
Hope someone can help,
Pim
The issue was that the role should start with ROLE_. It is possible to override it though but this was the use case.
Related
How can I fetch the currently logged in User from anywhere within the Backend code? For example I have an EventSubscriber class and want to fetch it from there.
How can I do that w/o the help of i.e. AbstractController?
Symfony AbstractController is the core of most Controllers. Including EasyAdmin crud controller (XXXCrudController) extends AbstractController so you can access the same methods.
One of those is getUser() which return the current logged in user.
* Get a user from the Security Token Storage.
*
* #return UserInterface|null
*
* #throws \LogicException If SecurityBundle is not available
*
* #see TokenInterface::getUser()
*/
protected function getUser()
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return null;
}
// #deprecated since 5.4, $user will always be a UserInterface instance
if (!\is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return null;
}
return $user;
}
So when trying to get the logged used in a controller, just use this method.
If you want to get the same thing, but for example in a service, you can basically do the same as what the method actually does by using the service injection with TokenStorageInterface to access the TokenStorage service which can get the current user.
So in your event subscriber, add TokenStorageInterface in your constructor to use it to first get the token and then your user. You may have to add another check to see if there is an user logged in (by checking if there is a token for example)
//YourService.php
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
private $tokenStorage
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function yourMethod()
{
//get token then user
$user = $tokenStorage->getToken()->getUser();
}
So, I've made an app with its own controller and entities. I've just installed the FOSUserBundle following the guide and I want to connect my app to the login, register, etc. I tried using the function 'path'. This is what looks like... 'href="{{path(menu.url)}}' ok, so I get an error: 'such route does not exist'. I decided to create another controller, this one called FosController, next to my previous one the DefaultController, I extended the FosController from SecurityController(login fosuserbundle) and decided to create with annotation a route called login and connect it to the parent method...
class FosController extends SecurityController
{
/**
* #Route("/hospitallogin", name="login")
* #Method({"GET", "POST"})
*
* #param Request $request
*
* #return Response
*/
public function login2Action(Request $request) {
parent::loginAction($request);
}
}
And I get this error:
The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?
I dont know what else to do, thanks for your time.
I decided to use this though it's not correct...
public function indexAction()
{
return $this->redirect('http://localhost:8000/login');
}
I have a controller which handles a GET request. I need to set requirement parameters for GET request, e.g.: 'http://localhost/site/main?id=10&sort=asc
My controller class
class IndexController extends Controller {
` /**
* #Route
* (
* "/site/main",
* name="main"
* )
*
* #Method("GET")
*/
public function mainAction(Request $request)
{
return new Response('', 200);
}
}
How could I do that?
UPD: I need to set requirement for URL parameters like
id: "\d+",
sort: "\w+"
Etc.
The same as symfony allows to do with POST request.
You can specify the requirements in the "#Route" annotation like this:
class IndexController extends Controller {
` /**
* #Route
* (
* "/site/main",
* name="main",
* requirements={
* "id": "\d+",
* "sort": "\w+"
* })
* )
*
* #Method("GET")
*/
public function mainAction(Request $request)
{
return new Response('', 200);
}
}
#Method is what you need http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html#route-method
If you try to use this route with POST, you will have 404
I couldn't understand your question well.
However, if what you need is to set up a filter mechanism for the GET method parameters as it is already available for the URL using route requirements, I think there is no ready to use tools for this in the Route component, as commented #Yoshi.
I had to do this kind of work myself and used this. I hope it helps you too
public function indexAction(Request $request)
{
// Parameter names used in the current request
$current_request_params=array_keys($request->query->all());
// $ALLOWED_INDEX_PARAMS should be declared as Class static property array and hold names of the query parameters you want to allow for this method/action
$unallowed_request_params=array_diff($current_request_params,PersonController::$ALLOWED_INDEX_PARAMS);
if (!empty($unallowed_request_params))
{
$result=array("error"=>sprintf("Unknown parameters: %s. PLease check the API documentation for more details.",implode($unallowed_request_params,", ")));
$jsonRsp=$this->get("serializer")->serialize($result,"json");
return new Response($jsonRsp,Response::HTTP_BAD_REQUEST,array("Content-Type"=>"application/json"));
}
// We are sure all parameters are correct, process the query job ..
}
I'm trying to use #Security annotations for my routes. Like this:
/**
* #return Response
* #Route("/action")
* #Security("has_role('ROLE_USER')")
* #Template()
*/
public function someAction()
{
return array();
}
When the security restriction fires an exception, I get the message Expression "has_role('ROLE_USER')" denied access.
This is not acceptable to be shown to the end user, so I'm trying to find a way to customize the message for annotation.
Simple workaround is to not to use #Secutity annotations and write code like these:
/**
* #return Response
* #Route("/action")
*
* #Template()
*/
public function someAction()
{
if (!$this->get('security.context')->isGranted('ROLE_USER')) {
throw new AccessDeniedException('You have to be logged in in order to use this feature');
}
return array();
}
But this is less convenient and less readable.
Is it possible to write custom message to #Security annotations?
As soon as I realized that this is not possible, I have made a pull request to the Sensio FrameworkExtra Bundle to make this possible.
This PR allows to customize displayed message by specifying the message parameter like
#Security("has_role('ROLE_USER')",message="You have to be logged in")
how do i get the password eneterd via a costum user provider. I found this question:
Symfony Security / Custom User Provider : How to get the login password within the custom User Provider?
Which says i would have to overrise the loadUserByName method, and add the password as a parameter, the issue is that i cannot find the file he overrides:
security_listeners.xml
Where is this file located?
I found the:
$user = $this->userProvider->loadUserByUsername($username);
call in the DOAAuthenticationProvider, and i see that the method takes:
protected function retrieveUser($username, UsernamePasswordToken $token)
as augments, i asume i need to pass the password there, and then pass it to loadUserByName, which calls my costum userprovider method.
Any help on how to achieve this is appreciated.
You can create an AuthenticationHandler on your bundle. and the on successful login fetch the password from the token.
in services.yml:
security.authentication.success_handler:
class: Wix\UserBundle\EventListener\AuthenticationHandler
arguments: ["#security.http_utils", {}]
tags:
- { name: 'monolog.logger', channel: 'security' }
And sample class:
class AuthenticationHandler extends DefaultAuthenticationSuccessHandler
{
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* #param Request $request
* #param TokenInterface $token
*
* #return Response never null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$response = new RedirectResponse('/');
$response->setStatusCode(200);
return $response;
}
}