How to get User object in Doctrine event subscriber in Zend Expressive - php

Based on https://github.com/DamienHarper/DoctrineAuditBundle I'm trying to develop audit module for my Zend Expressive application.
But I don't know how to get the User data (id) within audit logic.
I see that $user is passed as request attribute in
vendor/zendframework/zend-expressive-authentication/src/AuthenticationMiddleware.php, but this doesn't make it available via
$container->get(\Psr\Http\Message\ServerRequestInterface::class)->getAttribute(\Zend\Expressive\Authentication\UserInterface::class);

You might want to read again about the concept of middleware. In short, expressive has a middleware stack and depending on a request, it sends a the request through specific layers of middleware.
In your example the request goes through AuthenticationMiddleware. So if you have this as your pipeline:
$app->pipe(AuthenticationMiddleware::class);
$app->pipe(AuditMiddleware::class);
The request goes first through the AuthenticationMiddleware, which makes the UserInterface available in the request, and next through the AuditMiddleware.
In your AuditMiddleware and all middlewares after the AuthenticationMiddleware you can access the UserInterface like this:
function (ServerRequestInterface $request, RequestHandlerInterface $handler)
{
$user = $request->getAttribute(UserInterface::class);
// Do stuff here
return $handler->handle($request);
}
So in your case you probably need to write an AuditMiddleware that grabs the user from the request after the AuthenticationMiddleware and injects it in your Audit module.

Related

Laravel form request - $this->user() vs. auth()->user() in authorization

Can anyone explain if there is a reason why we should not be using getting the authenticated user within a from request authorize method, via the Auth::user() or auth()->user() helpers vs. the $this->user() method as suggested in docs?
https://laravel.com/docs/5.8/validation#authorizing-form-requests
In my case, I am trying to unit test a form request and auth()->user() allows me to retrieve the user whereas $this->user() does not as I am not making a full request. I am just creating the form request object for my test.
public function setUp(): void
{
parent::setUp();
$this->subject = new \App\Http\Requests\OrderStoreRequest();
}
// Acting as has no effect when manually creating the orderStoreRequest object
public function testAuthorize()
{
$this
->actingAs(\factory(User::class)->create())
->assertTrue($this->subject->authorize());
}
ActingAs() is calling the Laravel Auth system, which in the request lifecycle is put into the request (See). Since you are just calling your request without this lifecycle, you will never get anything injected into the Request.
For your code to work, you need to set the UserResolver. This can be done like so.
$this->subject->setUserResolver(function () use($user) {
return $user;
});
For ease of usage, i would highly recommend doing Laravel feature tests instead of unit testing. You are gonna fight your way through a lot of approaches, there is not meant to be called without the Laravel lifecycle. Which you will get doing call() and json() on the app.

Rest API or Semi Rest API

I have a controller for ProductController. I have 4 standard methods bound to respective HTTP methodslike
public function index() // GET
public function create() // POST
public function update() // PUT
public function destroy() //DELETE
So far so good, but i have to make few other functions like getProductsByCategory, getProductsAttributes() etc etc. After implementing this, Will my API still be called REST ? If not than how can i handle these requirements
Thanks
Resource URI for getProductsByCategory(...):
GET /products?category=books HTTP/1.1
Host: service.org
Resource URI for getProductsAttributes():
GET /products/bmw-528i/attributes HTTP/1.1
Host: service.org
How you implement handling of these request URIs is a implementation detail. If you are using some framework, you can do something like this:
Routes::add("/products/{product-id}/attributes", function($request, $response) {
// do something here
});
But it is a detail that can not affect RESTfullness of your service.
First off, REST is not a strict standard. The methods you posted comply the REST conventions but a REST service must have several other properties. The two most important ones are:
statelessness: no session, no cookies, authorization on a per-request basis
GET requeste never change any resource
There are other ones, feel free to edit or add in the comments.
The way i see such operations on resources implemented most of the time is:
/<resource-name>/<product-id>/<operation>
For example:
GET /product/<product-id>
GET /product/<product-id>/related
POST /product/<product-id>/purchase
GET /categories/tools

Controller as Service - How to pass and return values in an advanced case?

Using Symfony, I am displaying a table with some entries the user is able to select from. There is a little more complexity as this might include calling some further actions e. g. for filtering the table entries, sorting by different criteria, etc.
I have implemented the whole thing in an own bundle, let's say ChoiceTableBundle (with ChoiceTableController). Now I would like to be able to use this bundle from other bundles, sometimes with some more parametrization.
My desired workflow would then look like this:
User is currently working with Bundle OtherBundle and triggers chooseAction.
chooseAction forwards to ChoiceTableController (resp. its default entry action).
Within ChoiceTableBundle, the user is able to navigate, filter, sort, ... using the actions and routing supplied by this bundle.
When the user has made his choice, he triggers another action (like choiceFinishedAction) and the control flow returns to OtherBundle, handing over the results of the users choice.
Based on these results, OtherBundle can then continue working.
Additionally, OtherOtherBundle (and some more...) should also be able to use this workflow, possibly passing some configuration values to ChoiceTableBundle to make it behave a little different.
I have read about the "Controller as Service" pattern of Symfony 2 and IMHO it's the right approach here (if not, please tell me ;)). So I would make a service out of ChoiceTableController and use it from the other bundles. Anyway, with the workflow above in mind, I don't see a "good" way to achieve this:
How can I pass over configuration parameters to ChoiceTableBundle (resp. ChoiceTableController), if neccessary?
How can ChoiceTableBundle know from where it was called?
How can I return the results to this calling bundle?
Basic approaches could be to store the values in the session or to create an intermediate object being passed. Both do not seem particularly elegant to me. Can you please give me a shove in the right direction? Many thanks in advance!
The main question is if you really need to call your filtering / searching logic as a controller action. Do you really need to make a request?
I would say it could be also doable just by passing all the required data to a service you define.
This service you should create from the guts of your ChoiceTableBundleand let both you ChoiceTableBundle and your OtherBundle to use the extracted service.
service / library way
// register it in your service container
class FilteredDataProvider
{
/**
* #return customObjectInterface or scallar or whatever you like
*/
public function doFiltering($searchString, $order)
{
return $this->filterAndReturnData($searchString, $order)
}
}
...
class OtherBundleController extends Controller {
public function showStuffAction() {
$result = $this->container->get('filter_data_provider')
->doFiltering('text', 'ascending')
}
}
controller way
The whole thing can be accomplished with the same approach as lipp/imagine bundle uses.
Have a controller as service and call/send all the required information to that controller when you need some results, you can also send whole request.
class MyController extends Controller
{
public function indexAction()
{
// RedirectResponse object
$responeFromYourSearchFilterAction = $this->container
->get('my_search_filter_controller')
->filterSearchAction(
$this->request, // http request
'parameter1' // like search string
'parameterX' // like sorting direction
);
// do something with the response
// ..
}
}
A separate service class would be much more flexible. Also if you need other parameters or Request object you can always provide it.
Info how to declare controller as service is here:
http://symfony.com/doc/current/cookbook/controller/service.html
How liip uses it:
https://github.com/liip/LiipImagineBundle#using-the-controller-as-a-service

Properly implementing resource authorization for a RESTful API

My team has an API we wrote in PHP using the Slim Framework. It's being consumed by a web app and a third party mobile app.
We use the standard OAuth 2 workflow to provide access to our resources. We can see if someone is a sending a valid access token along with the API request and that part of the flow makes sense.
The stumbling block we're running into is how to most efficiently authorize access to a resource depending on the permissions of the user associated with the access token.
For the sake of simplicity let's say that we just want to ensure that the owner of the resource in question matches the owner of the access token. To me that seems like something that a route middleware would handle, for every request, before processing the request make sure that the resource owner ID matches that of the access token.
The problem in my mind is that resource permissions aren't necessarily consistent across all routes, a route isn't necessarily going to have an ID in the same section of the URI, it might not have an ID in the URI at all, etc. etc.
My current solution has been to have an authorization utility class that takes in an email and checks it against the user that's currently "logged in" (token owner).
class Authorization() {
checkResourcePermissions($email) {
if (loggedInUser->email == $email) {
return true;
}
}
}
Obviously this is a simplification, but what this means is that since a route middleware won't have the context of a request until that request goes through I will need to call this authorization method inside of every API route, responding with an error if necessary, etc. Our API is fairly large, essentially boiling this down to a large amount of copy and paste which always makes me very nervous.
What am I missing?
I'd suggest going the way using a 'normal' MiddleWare:
First, let's assume you have a class 'user', where you implement your logic to access various types of resources or has specific permissions, given only the $app. It can access the request and get any information from a the HTTP request necessary to identify a single user.
class User{
private $userid=false;
public function __construct($app){
$this->app=$app;
$users_mail=$this->app->request->post('email');
//SELECT user_id FROM USERSTABLE WHERE email = $users_mail
//$this->userid=user_id
}
public function ensureHasAccess($resourceID,$redirect='/noPermission'){
if(!/*some DB Query which returns true if this user may access the given $resourceID*/)
$this->app->redirect($redirect)
}
public function ensureHasRole($role) {
//some other permission checking, like ensureHasAccess();
}
public function ensureIsUser(){
//do Something
}
}
Next, you'll need a MiddleWare which is run before the route is dispatched, and creates an User-Object:
class UserPermissionMiddleware extends \Slim\Middleware{
public function call(){
$this->app->request->user = new User($app);
$this->next->call();
}
}
Then, simply add this Middleware:
$app->add(new UserPermissionMiddleware());
Now, you can modify every route to check the access to a resource, while the resource id (of course) still needs to be supplied by hand, according to the route-specific code:
$app->get('/resource/:id',function($resourceId) use ($app){
$app->request->user->ensureHasAccess($resourceId);
//deliver resource
});
If the user is not allowed to access this resource, the whole process is interrupted (Note that redirect() is implemented using Exceptions!) and another route is executed. You'll still need some code in every single Route, but if there's no consistent way to determine the resource to be acccessed, there'll be no way around this.
Hope this helps you so far. If you have any Questions, feel free to ask!

Working of Zend_Controller_Plugin_ActionStack

I am currently reading "Zend Framework 1.8
Web Application Development" written by "Keith Pope". In that he tells us to use 'ActionStack' so that the Controller for the category top-level menu will be called on every request. The source code for the the plugin is :
class SF_Plugin_Action extends Zend_Controller_Plugin_Abstract
{
protected $_stack;
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$stack = $this->getStack();
// category menu
$categoryRequest = new Zend_Controller_Request_Simple();
$categoryRequest->setControllerName('category')
->setActionName('index')
->setParam(
'responseSegment',
'categoryMain'
);
// push requests into the stack
$stack->pushStack($categoryRequest);
}
public function getStack()
{
if (null === $this->_stack) {
$front = Zend_Controller_Front::getInstance();
if (!$front->hasPlugin(
'Zend_Controller_Plugin_ActionStack'
)) {
$stack = new Zend_Controller_Plugin_ActionStack();
$front->registerPlugin($stack);
} else {
$stack = $front->getPlugin('ActionStack');
}
$this->_stack = $stack;
}
return $this->_stack;
}
}
I have read the code for 'ActionStack' plugin. In the 'postDispatch' function it saves the current request and then in the 'forward' function it changes the current request's controller, action and also set parameters. Then what will happen to the current request ? How it will be executed ?
Also I heard ActionStack is evil. As I am a newbie I didn't understand most of it, as he did not explained it(for newbies). Why ActionStack is evil ?
ActionStack is evil as it promotes a bad practice: tying view-related logic to controllers. Additionally, it has a huge, negative impact on performance.
Typically, ActionStack is used to develop "widgetized" sites. You set up a list of widgets you need, and map them to individual controller actions, and then loop through the stack. The design flaw with this is that you're now executing N different controllers -- when, really, ONE controller is all you should use. Each individual controller should be able to map the incoming request to the necessary view and models. Instead, you're now basically executing an MVC triad simply to get back a bit of content.
The performance implications come from the fact that you now have to store the previous results in memory, and then re-dispatch -- which means running all pre/post dispatch plugins again, potentially having conflicts in state, and more.
The better approach is to have model-aware view helpers. You can use action helpers to marshal the appropriate models and inject the helpers, and then in your view scripts and/or layout, you simply invoke them. This honors an appropriate separation of concerns, and does not have the same performance implications.
(In ZF2, this marshaling is far easier, as you can define factories for individual helpers -- as such, you can simply use them in your view scripts, and not have to do anything special in the controllers at all in order to deliver widgetized content.)
It is the answer to my first question. As the action stack will be executed last (in the post dispatch) the current response object will be holding all content that got rendered for the request the user made, and the action stack will append the data to it. Hence the user will get content that he asked for + content that got rendered due to action stack
In your example. The front controller will start the execution of the current request and fire the routeStartup, routeShutdown and dispatchLoopStartup events. The dispatchLoopStartup event will call your plugin and your plugin will add a request object to the action stack.
Now the front controller will dispatch the current request, set the isDispatched flag of the current request to true and fire the postDispatch event. Now the Action stack plugin will get called. The front controller will pass the current request object to the Action Stack plugin as an argument and the Action stack plugin will update the controller, module, action properties of the current request object and set its isDispatched flag to false (Forward method).
Now the front controller will check the isDispatched flag of the current request object and since it was reset by the Action Stack plugin start the dispatch process again. And now your new request will get executed.
In short the front controller dispatches the current request, the action stack plugin resets the values of current request and the dispatch loop stars again.

Categories