Laravel: 6.18.1
PHP: 7.4
Middleware in laravel is creating this error
{
"message": "Call to undefined method Symfony\\Component\\HttpFoundation\\Response::withCookie()",
"exception": "Symfony\\Component\\Debug\\Exception\\FatalThrowableError",
Code which is giving me error
if (!$request->hasCookie('ppl') || ($request->hasCookie('ppl') && $ppl_cookie->ppl_id != $ppl->ppl_id)) {
if (Auth::check()) {
Event::dispatch('ppl.updated', [Auth::user(), $ppl]);
}
return $next($request)->withCookie(cookie()->forever('ppl', $ppl));
}
I don't understand issue. cookie is not stored in browser
Edit
Middleware class
<?php
namespace App\Http\Middleware;
use Closure;
use App\System\Models\People;
use App;
use Event;
use Auth;
use Illuminate\Support\Facades\URL;
use Session;
class VerifyPeople
{
protected $app;
public function __construct()
{
$this->app = app();
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$People_cookie = json_decode($request->cookie('People'));
if (!empty($request->route()) && in_array($request->route()->uri(), $this->excepts)) {
return $next($request);
}
if ($request->getHost()) {
$domain_url = cleanUrl($request->getHost());
$People = People::where('People_url', '=', $domain_url)->remember(LONG_TERM_CACHE_TIMEOUT)->cacheTags(TAG_LONGTERM_DATA)->first();
} elseif ($request->hasCookie('People')) {
$People = $People_cookie;
}
if (empty($People)) {
$People = People::where("People_id", People::DEFAULT_People)->remember(LONG_TERM_CACHE_TIMEOUT)->cacheTags(TAG_LONGTERM_DATA)->first();
}
$this->app->singleton('People', function () use ($People) {
return $People;
});
if (!$request->hasCookie('People') || ($request->hasCookie('People') && $People_cookie->People_id != $People->People_id)) {
if (Auth::check()) {
Event::dispatch('People.updated', [Auth::user(), $People]);
}
return $next($request)->withCookie(cookie()->forever('People', $People));
}
return $next($request);
}
}
In the middleware when you do dd($next($request)); it is supposed print an instance of Illuminate\Http\Response which can access to ResponseTrait and withCookie method within the trait..
In your case, the instance of Symfony\Component\HttpFoundation\Response is giving error which is extended by Illuminate\Http\Response but doesn't use ResponseTrait. That's the reason withCookie method didn't found.
There may be several reasons such as, before this middleware is executed - another middleware is modifying default response with Symfony response.
Here is the withCookie method.
public function withCookie($cookie)
{
if (is_string($cookie) && function_exists('cookie')) {
$cookie = call_user_func_array('cookie', func_get_args());
}
$this->headers->setCookie($cookie);
return $this;
}
What it does is calling setCookie method of headers. What you may do is;
Replace
return $next($request)->withCookie(cookie()->forever('People', $People));
with
$response = $next($request);
$response->headers->setCookie(cookie()->forever('People', $People));
return $response;
While looking; i found a similar case to yours
Related
I am making simple api wrapper, so all requests to https://example.comA/api/me must be catched on kernel response level and forwarded to https://api.example.comB/me and all was fine however I cannot get the currently logged in User in that kernel response because it returns null:
namespace App\Manager\Api\Event;
use App\Provider\Core\Api\CoreApi;
use GuzzleHttp\Exception\BadResponseException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\Security\Core\Security;
class ApiWrapperEventListener
{
private $coreApi;
private $security;
public function __construct(CoreApi $coreApi, Security $security)
{
$this->coreApi = $coreApi;
$this->security = $security;
}
public function onKernelResponse(ResponseEvent $event)
{
if (!$event->isMasterRequest()) return;
$request = $event->getRequest();
if ('/api' === substr($request->getPathInfo(), 0, 4)) {
dump($this->security->getUser()); // returns NULL
die;
try {
$response = $this->coreApi->call($request->getMethod(), $request->getPathInfo(), json_decode($request->getContent(), true) ?? []);
$event->setResponse(new JsonResponse($response));
} catch (BadResponseException $error) {
dump($error);
die;
}
}
}
}
I guess Symfony is firing those events before I get the User, is there a way to get this right?
I have to note that in other places like controllers or services I get the User right.
Ok i know what was the problem.
The response controller event has no User when 404 or 500 given.
In my case I was catching 404, passing to listener and modifying the request to 200.
This approach wasn't good, so i decided to move this to Controller itself.
/**
* #Route("/api/v1/{uri}", name="api", requirements={"uri"=".+"})
*/
public function api(
Request $request,
CoreApi $coreApi
):Response
{
try
{
$response = $coreApi->call($request->getMethod(), $request->getPathInfo(), json_decode($request->getContent(), true) ?? []);
return new JsonResponse($response);
}
catch(BadResponseException $error)
{
return new Response($error->getResponse()->getBody()->getContents(), $error->getResponse()->getStatusCode());
}
}
I have multiple users with multiple permissions. A user can belong to the only single role but that role can have multiple permissions like create, read, update, delete. And I have a RoleMiddleware. I am authenticating the user in roleMiddleware. But how can I protect routes in RoleMiddleware against a specific user?
For Example, I have a route create-case which can only be accessed by the operator or by Admin else everyone redirects to 404 error how Can I deal with it in RoleMiddleware.
I have written basic code for authentication where every user with their roles is authenticated but I am getting how can I code in middleware so ever route when a user hits it may go to the RoleMiddleware where middleware Authenticate route to the Role and then give him the access.
Role Middleware
class RoleMiddleware
{
public function handle($request, Closure $next, $permission = null)
{
if (Auth::check() === false)
{
return redirect('login');
}
elseif (Auth::check() === true)
{
$roles = Role::all()->pluck('slug');
if (is_null($request->user()) )
{
abort(404);
}
if (!$request->user()->hasRole($roles))
{
abort(404);
}
if ($request->user())
{
if ($request->user()->hasRole($roles))
{
return $next($request);
}
}
}
}
}
Case Controller:
<?php
namespace App\Http\Controllers\Cases;
use App\Http\Controllers\Controller;
use App\Http\Requests\CaseStoreRequest;
use Illuminate\Support\Facades\Auth;
use Session;
class CaseController extends Controller
{
use DropzoneFileUploadTraits;
public function __construct()
{
$this->middleware('role');
}
public function index()
{
$data['portal'] = Portal::all();
$data['operators'] = Operator::all();
return view('case', $data);
}
public function caseList()
{
$user = new User();
$isAdmin = $user->isAdmin();
$loggedIn = Auth::id();
$cases = Cases::with('patients', 'portal')
->when(!$isAdmin, function ($query) use ($loggedIn) {
return $query->where('user_id', $loggedIn);
})->orderBy('created_at', 'desc')->get();
$data['cases'] = $cases;
return view('case_list', $data);
}
}
Route:
Route::get('create-case', 'Cases\CaseController#index')->name('create-case');
Route::post('case-submit', 'Cases\CaseController#caseSubmit')->name('case-submit');
Route::post('edit-patient-case-submit', 'Cases\CaseController#editPatientCaseSubmit')->name('edit-patient-case-submit');
Route::get('case-list', 'Cases\CaseController#caseList')->name('case-list');
Best way to do that in a clean manner would be to create policies on the targeted entities.
Laravel policies allow you to :
Bind a route authorization logic to a policy action
Easily call a policy action result from anywhere else in the project (views, controllers and so on).
The subject is well covered in Laravel documentation so I suggest you go there and take a look. Do not forget to register the policy and bind it to your model.
Apart from that this should do the trick.
class CasePolicy
{
use HandlesAuthorization;
public function create(User $user){
$roles = ['operator','Admin']
return $user->hasRole($roles);
}
}
Then in your route file :
Route::get('create-case', 'Cases\CaseController#index')->name('create-case')->middleware('can:create,App\Case');
I haved just learned and implement Gate and Policy hope this is correct Because its working for me. Great concept thanks.
Route::get('create-case', 'Cases\CaseController#index')->name('create-case')->middleware('can:create-case,App\Model\Case');
Gate:
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
User::class => CreateCase::class
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('create-case','App\Policies\CreateCase#create_case');
}
}
Policy
class CreateCase
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* #return void
*/
public function __construct()
{
//
}
public function create_case(User $user){
if($user->hasRole(['admin']) ||$user->hasRole(['operator']) && $user->hasPermissionTo('create')){
return true;
}else
return false;
}
}
I'm working on a Laravel package it is working when I use it in my Laravel project, but when I want to test it with Orchestra Testbench I always get current route null inside middlware.
Test directory on Github: https://github.com/yoeunes/larafast/tree/master/tests
Base TestCase:
class TestCase extends Orchestra\Testbench\TestCase
{
protected function getEnvironmentSetUp($app)
{
$kernel = app('Illuminate\Contracts\Http\Kernel');
$kernel->pushMiddleware(\Illuminate\Session\Middleware\StartSession::class);
$kernel->pushMiddleware(\Yoeunes\Larafast\Middlewares\BlacklistRoutes::class);
}
}
WebControllerTest:
class WebControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
/** #var \Illuminate\Routing\Router $router */
$router = $this->app['router'];
$router->resource('lessons', 'Yoeunes\Larafast\Tests\Stubs\Controllers\Web\LessonController');
}
/** #test */
public function it_show_create_page()
{
/** #var TestResponse $response */
$response = $this->call('get', '/lessons/create');
dd($response);
$response->assertSuccessful();
$response->assertSee('<title>lessons create | Larafast</title>');
$response->assertSee('<i class="fa fa-plus-circle"></i> lessons create');
$response->assertSee('<form method="POST" action="http://localhost/lessons" accept-charset="UTF-8" enctype="multipart/form-data">');
}
}
BlacklistRoutes Middleware:
class BlacklistRoutes
{
public function handle($request, Closure $next)
{
dd(app('router')->getCurrentRoute()); // always get null
if (null !== ($route = app('router')->getCurrentRoute())
&& is_a($controller = $route->getController(), Controller::class)
&& in_array($route->getActionMethod(), $controller->getBlacklist(), true)) {
throw new BlacklistRouteException();
}
return $next($request);
}
}
According to Orchestra Testbench Github repository :
AFAIK global middleware is resolved before route is resolved,
therefore getCurrentRoute() shouldn't be available yet.
So the resolve my problem I can access to current route from within the controller constructor like this:
public function __construct()
{
$this->middleware(function ($request, Closure $next) {
if (null !== ($route = app('router')->getCurrentRoute())
&& is_a($controller = $route->getController(), Controller::class)
&& in_array($route->getActionMethod(), $controller->getBlacklist(), true)) {
throw new BlacklistRouteException();
}
});
}
I have made the following custom guard:
<?php
namespace App\Auth;
use Illuminate\Http\Request;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
class LicenseGuard implements Guard
{
use GuardHelpers;
protected $request;
public function __construct(LicenseUserProvider $provider, Request $request)
{
$this->provider = $provider;
$this->request = $request;
}
public function user ()
{
// If we've already retrieved the user for the current request we can just
// return it back immediately. We do not want to fetch the user data on
// every call to this method because that would be tremendously slow.
if (!is_null($this->user))
return $this->user;
$user = null;
$licenseKey = $this->request->json('license_key');
if (!empty($licenseKey)) {
$user = $this->provider->retrieveByLicense($licenseKey);
}
return $this->user = $user;
}
public function validate (Array $credentials = [])
{
/* Validate code */
}
}
?>
In my middleware i have defined the following:
<?php
if($this->auth->guard($guard)->quest())
return response('You have entered an unknown license key', 401);
The error that i am getting is:
Fatal error: Call to undefined method App\Auth\LicenseGuard::quest()
I am using the default GuardHelper trait which has the "quest" method, i just can't find out why this is happening.
I am using PHP7 and Lumen 5.2
Not sure what you are doing there my friend, but I assume quest "isn't the droids you are looking for".
I have written this router after creating everything using the official:
$ php composer.phar create-project slim/slim-skeleton [my-app-name]
this is my routes.php file
<?php
require_once(__DIR__."/../bootstrap.php");
// Routes
class OwnsPost
{
/**
* Example middleware invokable class
*
* #param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* #param \Psr\Http\Message\ResponseInterface $response PSR7 response
* #param callable $next Next middleware
*
* #return \Psr\Http\Message\ResponseInterface
*/
public function __invoke($request, $response, $next)
{
if($request->getQueryParams() && $request->getQueryParams()['pid']){
$pid = intval($request->getQueryParams()['pid']);
if($pid == 0){
$this->logger->info("illegal pid call");
return false;
}
$cpost = get_post($pid);
if($cpost->post_author != get_current_user()){
$this->logger->info("wrong current user, tried accessing postid " . $cpost->ID . " with user ". get_current_user());
return false;
}
}else{
$this->logger->info("illegal pid call");
return false;
}
// $response->getBody()->write('BEFORE');
$response = $next($request, $response);
// $response->getBody()->write('AFTER');
return $response;
}
}
$app->get('/campaignedit/setcharitable/{id}', function ($request, $response, $id) {
// Sample log message
$this->logger->info("setcharitable '/' route " . $id);
// Render index view
return $this->renderer->render($response, 'index2.php', $id);
})->add( new OwnsPost() );
$this->logger works in the routing section, but not in the middleware section.
I get an
Fatal error: Call to a member function info() on a non-object in
/var/www/html/japi/src/routes.php on line 33
There is no member variable $logger in your middleware class. So, first add one.
protected $logger;
Next, add a constructor that accepts $logger as an argument.
public function __construct($logger)
{
$this->logger = $logger;
}
Last, when you initialize your middleware, pass the instance of your Logger.
$ownsPost = new OwnsPost($this->logger);
I need obviously to call to
global $app