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

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.

Related

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

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.

Is there a way to act as a user inside of Laravel without hitting the database?

I'm writing unit tests for an API using PHPUnit and Laravel. Most functions I'm testing require that the user is authenticated before the function can be ran. The user data is stored in one table, and their permissions are stored inside of another table. I can fake the user object inside of Laravel, but I need to be able to also pull the corresponding permissions from the other table without having to hit the database like the dingo router currently is doing.
Currently running Laravel 5.8 and PHPUnit 8.1.5. I currently have the users object that I generated from a Laravel factory saved to a text file. I am able to pass that to a function called "actingAsApi" (found on Github, code below) and that allows me to authenticate as that user. However, the function is still going out and getting all permissions for that user from the database. I'm trying to mock or fake the permissions object it is pulling somewhere so that it doesn't need to hit the database at all. I also tried using the built in Passport functions for Passport::actingAs, and those did not work either as they were still hitting the DB (and not really working anyways).
actingAsApi (inside of TestCase.php)
protected function actingAsApi($user)
{
// mock service middleware
$auth = Mockery::mock('Dingo\Api\Http\Middleware\Auth[handle]',
[
Mockery::mock('Dingo\Api\Routing\Router'),
Mockery::mock('Dingo\Api\Auth\Auth'),
]);
$auth->shouldReceive('handle')
->andReturnUsing(function ($request, \Closure $next) {
return $next($request);
});
$this->app->instance('Dingo\Api\Http\Middleware\Auth', $auth);
$auth = Mockery::mock('Dingo\Api\Auth\Auth[user]',
[
app('Dingo\Api\Routing\Router'),
app('Illuminate\Container\Container'),
[],
]);
$auth->shouldReceive('user')
->andReturnUsing(function () use ($user) {
return $user;
});
$this->app->instance('Dingo\Api\Auth\Auth', $auth);
return $this;
}
Test inside of my Test file
public function testActAs() {
$user = 'tests/users/user1.txt';
$this->actingAsApi($user);
$request = new Request;
$t = new TestController($request);
$test = $t->index($request);
}
I expect the actingAsApi function to allow me to also pass in the mock permissions data that corresponds to my mock user object data from the file, but instead it is hitting the database to pull from the permissions table.
EDIT:
So i've been playing around with doing mock objects, and i figured out how to mock the original controller here:
$controlMock = Mockery::mock('App\Http\Controllers\Controller', [$request])->makePartial();
$controlMock->shouldReceive('userHasPermission')
->with('API_ACCESS')
->andReturn(true);
$this->app->instance('App\Http\Controllers\Controller', $controlMock);
but now I can't figure out how to get my call from the other controllers to hit the mocked controller and not a real one. Here is my code for hitting an example controller:
$info = $this->app->make('App\API\Controllers\InfoController');
print_r($info->getInfo('12345'));
How can i make the second block of code hit the mocked controller and not standup a real one like it does in its constructor method?
Finally came on an answer, and it is now fixed. Here's how I did it for those wondering:
$request = new Request;
$controlMock = m::mock('App\API\Controllers\InfoController', [$request])->makePartial();
$controlMock->shouldReceive('userHasPermission')
->with('API_ACCESS')
->andReturn(true);
print_r($controlMock->getInfo('12345'));
Basically, I was trying to Mock the original API controller, and then catch all of the calls thrown at it. Instead, I should've been mocking the controller I'm testing, in this case the InfoController. I can then catch the call 'userHasPermission', which should reach out to the Controller, but I am automatically returning true. This eliminates the need for hitting the database to receive permissions and other info. More information on how I solved it using Mockery can be found here: http://docs.mockery.io/en/latest/cookbook/big_parent_class.html. As you can see, this is referred to as a 'Big Parent Class'. Good luck!

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

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!

Cannot expire cookie from a controller in CakePHP 2.x

I am building an API in CakePHP. I have a function that as part of its execution first destroys the cookies associated with the session. I am using the following code to do this.
public function new_thing () {
// I first call another controller to use functions from that controller
App::import('Controller', 'Person');
$PersonsController = new PersonsController;
// This function call is the problem
// This does not throw any errors but does not destroy the cookie as requested
$PersonsController->_kill_auth_cookie()
}
// This is from the Person controller, these are the functions used in the API
// This is the function that sets the cookies
public function _set_auth_cookie( $email ) {
setcookie(Configure::read('auth_cookie_name'), $email);
}
// this is the function that does not properly destroy the cookie from the API
// interestingly, it does work when called from in this controller
public function _kill_auth_cookie() {
setcookie(Configure::read('auth_cookie_name'), 'xxx', time()-7200);
}
I cannot get the API to properly expire the cookie that is created earlier in the session, I am not sure why. Additionally—what is maddening—is that the logs are empty and no error is being thrown of any kind, so I am not sure what to do next.
There is so much wrong in this code and concept…
DON'T instantiate controllers anywhere. It is plain wrong, broken by design and violates the MVC pattern. Only one controller should be dispatched by the framework itself based on the request; you don’t instantiate them manually.
An API using cookies? Well, not impossible but definitely not nice to work with. It’s possible but I’ve never seen one in the wild. I feel sorry for the person who has to implement it. See this question.
Why are you not using the CookieComponent? It has a built-in destroy() method to remove a cookie.
If you have an “auth” cookie, why are you not using CakePHP’s built-in Auth system? It will deal with all of that.
Use App::uses() not App::import() here
By convention, only protected functions should be prefixed with _
The first point is very likely the reason why cookie and sessions are messed up because the second controller instance initiates components again, and by this cookie and session maybe a second time as well. However, this can lead to “interesting” side effects.
I first call another controller to use functions from that controller
This is the evidence that your architecture is broken by design. The code that needs to be executed somewhere else; should be in a model method in this case. Or at least a component if there are controller-related things to be shared between different controllers.

Categories