I've got a middleware setup that's caching the HTML output of each public Controller request, in each of my controllers' __construct method.
public function __construct() {
$this->middleware('auth', ['except' => ['index', 'show']]);
$this->middleware('cache.get', ['only' => 'show']);
$this->middleware('cache.put', ['only' => 'show']);
}
The caching is working great, as expected, except for one thing: I've got Route-Model bindings setup in RouteServiceProvider.php for easy accessiblity of models in their respective controllers, like
public function boot(Router $router)
{
parent::boot($router);
$router->bind('posts', function($id) {
return \App\Article::findBySlugOrIdOrFail($id);
});
$router->bind('tags', function($name) {
return \App\Tag::where('name', $name)->firstOrFail();
});
$router->bind('artists', function($slug) {
return \App\Artist::findBySlugOrIdOrFail($slug);
});
Basically what's happening is even when pages are cached, I'm still getting a single query for each route where it's looking up that slug (SluggableInterface) or id. Wondering if there's a way to do this where the query doesn't occur when a route is cached? Or is that just not possible?
Thanks!
EDIT
Here's my caching middleware:
class GetCache
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$key = StringHelper::keygen($request->path());
if (Cache::has($key) && Auth::guest()) {
$content=Cache::get($key);
return response($content);
}
return $next($request);
}
}
class PutCache
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$key = StringHelper::keygen($request->path());
if (! Cache::has($key) && Auth::guest()) {
Cache::put($key, $response->getContent(), 600);
}
return $next($request);
}
}
Related
I created my request in which I validate the data. Some of this data I need to convert to JSON.
For this, I decided to create middleware. But when I try to get a request in the controller, it doesn't have anything I added in the middleware.
This seems to be because it is not my own request 'MyRequest $request' that gets into the middleware. How can this be resolved?
middlevare
class TransformData
{
/**
* #param $request
* #param Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$next($request);
$request->merge(['user_id' => \Auth::user()->id]);
$request->merge(['select_products' => json_encode($request->select_products)]);
return $request;
}
}
my request is called OfferRequest, there are just validation rules
controller
class BaseController extends Controller
{
public function __construct()
{
$this->middleware('transform.offer.data')->only('store');
}
}
public function store(OfferRequest $request)
{
$all = $request->all();
dd($all); // there is nothing here that I added in the middleware
}
I added the middleware to the kernel - protected $routeMiddleware
Update your Middleware like the following :
TransformData.php
class TransformData
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$request->merge([
'user_id' => \Auth::user()->id,
'select_products' => json_encode($request->select_products)
]);
return $next($request);
}
}
The above would work, but I would suggest you to use prepareForValidation() method link in form request class for update request data.
Using prepareForValidation() method you could add or update your request parameters.
OfferRequest.php
class AccountFilterRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
//your rules
}
/**
* Modify the input.
*/
public function prepareForValidation()
{
$this->merge([
'user_id' => \Auth::user()->id,
'select_products' => json_encode($request->select_products)
]);
}
}
prepareForValidation() method is executed before validation so you can add new data or update data and validate those.
When a user is not authenticated, Laravel redirects all request made to routes using auth::api middleware to the default laravel login page. However since I am building the API only, I want to return a 401 unauthorised response to any app that will be consuming my api. This should be simple but for some reason, I haven't been able to do just that. Here's what my code looks like
public function show(User $user)
{
if ($user->id == auth()->user()->id) {
// do something here
} else {
return response()->json([ 'status_message' => 'Unauthorised'], 401);
}
}
public function update(Request $request, User $user)
{
if ($user->id == auth()->user()->id) {
// do something here
} else {
return response()->json(['status_message' => 'Unathorised'], 401);
}
}
When I hit the endpoints calling these methods and the user isn't authenticated, I am redirected to the default laravel login page. How can I override this default behavior?
Call api.auth middleware in your routes as follows
$api->group(['middleware' => 'api.auth'], function ($api){
//your routes
});
Create a file Authenticate.php inside Middleware folder
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authenticate {
/**
* The authentication guard factory instance.
*
* #var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* #param \Illuminate\Contracts\Auth\Factory $auth
* #return void
*/
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
return response('Unauthorized.', 401);
}
return $next($request);
}
}
and in kernel.php which is inside http folder include the following
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
];
Problem solved guys. Here's how I did it, in the Handle.php file in app\Exceptions\ I added this code to the render function
if ($exception instanceof AuthenticationException) {
return response()->json(['status_message' => 'Unauthorised'], 401);
}
And that was it.
Note: this worked in laravel 5.8
I'm having issues with getting the user from an Illuminate Request ($request->user()).
$request->user() // whatever i do this keeps returning null
The PusherBroadcastDriver uses this and i can't edit third party code for obvious reasons..
I'm using Tymon\JWT to generate tokens and authenticate users with my application. I currently tried to do the following (check code)
AUTHENTICATION SERVICE PROVIDER:
public function boot()
{
$this->registerPolicies();
Auth::extend('jwt', function ($app, $name, array $config) {
return new JWTGuard($app['tymon.jwt'],
Auth::createUserProvider($config['provider']),
$app['request']);
});
}
MIDDLEWARE:
public class JWTRefreshMiddleware extends RefreshToken
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
*
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = parent::handle($request, $next);
$this->addUserToRequest();
return $response;
}
private function addUserToRequest()
{
$request = app('request');
$user = User::find(JWTAuth::getPayload()['user_id']);
$request->merge(['user' => $user]);
$request->setUserResolver(function () use ($user) {
return $user;
});
Auth::setUser($user);
}
}
But unfortunately the above didn't work.
Can someone point me in the right direction?
This is the final solution:
class JWTRefreshMiddleware extends RefreshToken
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
*
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = parent::handle($this->addUserToRequest($request), $next);
return $response;
}
private function addUserToRequest(\Illuminate\Http\Request $request): \Illuminate\Http\Request
{
$user = User::find(JWTAuth::manager()->decode(JWTAuth::getToken())['user_id']);
$request->merge(['user' => $user]);
$request->setUserResolver(function () use ($user) {
return $user;
});
Auth::setUser($user);
return $request;
}
}
It seems you are changing a variable, not the request instance, maybe you can try this.
public function handle($request, Closure $next)
{
$response = parent::handle($this->addUserToRequest($request), $next);
return $response;
}
private function addUserToRequest($request)
{
$user = User::find(JWTAuth::getPayload()['user_id']);
$request->merge(['user' => $user]);
$request->setUserResolver(function () use ($user) {
return $user;
});
Auth::setUser($user);
return $request;
}
I want to create two middleware to redirect the authenticated user, if it is an admin it will be redirected to the backoffice otherwise it will be redirected to a simple dashboard for simple users.
But I want to use only the users table without adding another table for the admins.
RedirectIfAuthenticated.php
<?php
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if (Auth::user()->role_id == 1)
{
return redirect('/admin/home');
}
return redirect('/dashboard');
}
return $next($request);
}
}
DashboardController.php
class DashboardController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('authFront.layoutAuthenticatedUser.dashboard');
}
}
web.php
Route::get('/us-admin', function () { return redirect('/admin/home'); })->name('admin.dashboard');
// Authentication Routes...
$this->get('login', 'Auth\LoginController#showLoginForm')->name('auth.login');
$this->post('login', 'Auth\LoginController#login')->name('auth.login');
$this->post('register', 'Auth\RegisterController#register')->name('auth.register');
$this->post('logout', 'Auth\LoginController#logout')->name('auth.logout');
// Change Password Routes...
$this->get('change_password', 'Auth\ChangePasswordController#showChangePasswordForm')->name('auth.change_password');
$this->patch('change_password', 'Auth\ChangePasswordController#changePassword')->name('auth.change_password');
// Password Reset Routes...
$this->get('password/reset', 'Auth\ForgotPasswordController#showLinkRequestForm')->name('auth.password.reset');
$this->post('password/email', 'Auth\ForgotPasswordController#sendResetLinkEmail')->name('auth.password.reset');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController#showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController#reset')->name('auth.password.reset');
Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
Route::get('/home', 'HomeController#index');
});
Route::get('/dashboard', 'DashboardController#index');
You need to establish user roles in this case, or have an optional boolean field on the users table for is_admin. Then in middleware it would simply be something like:
class RedirectIfNotAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (!Auth::user()->admin()) {
return redirect()->to('user-dashboard');
}
return $next($request);
}
}
So in your case I would create a separate middleware for each role post-authorization. Then you would apply AdminMiddleware to all routes prefixed with 'admin', SimpleUserMiddleware to all other routes, for example.
You can simply check roles in RedirectIfAuthenticated middleware which do already exist by default and restrict the routes by creating an admin middleware
In app/Http/Middleware/RedirectIfAuthenticated.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if (Auth::user()->role_id == 1)
{
return redirect('/admin/home');
}
return redirect('/dashboard');
}
return $next($request);
}
}
In app/Http/Middleware/AuthenticateAdmin.php
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class AuthenticateAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!Auth::check() || Auth::user()->role_id != 1)
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
return redirect()->guest('login');
}
}
return $next($request);
}
}
In app/Http/Kernal.php add this line in $routeMiddleware array
'admin' => \App\Http\Middleware\AuthenticateAdmin::class,
In routes/web.php
Route::middleware('admin')->prefix('admin')->group(function() {
Route::get('/home', 'HomeController#index')->name('admin.home');
});
In app/Http/Controllers/Auth/LoginController.php add this function
protected function authenticated(Request $request, $user)
{
if ($user->role_id == 1) {
return redirect()->intended(route('admin.home'));
}
return redirect()->intended(route('dashboard'));
}
Hope this helps you
I was facing an issue these days when I tried to pass arguments from my router to my middleware, to check if the authenticated user has the permissions to access that route.
How can I pass an argument from routes to the middleware?
I tried to do it and it works very well for me:
In my routes files:
Route::group(['prefix' => 'agenda', 'middleware' => 'auth', 'permissions' => 'user.create|user.delete'], function() {
//my routes here...
});
and inside the middleware:
class AuthMiddleware {
private $r;
private $guard;
public function __construct(Router $r, Guard $g)
{
$this->r = $r;
$this->guard = $g;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$route = $this->r->getCurrentRoute();
$action = $route->getAction(); //$action['permissions'] is the string received from the routes file.
}