Laravel owner middleware not working - laravel

I created a middleware (app/Http/Middleware/AbortIfNotOwner.php), this is code from another Stackoverflow post
<?php
namespace App\Http\Middleware;
use Closure;
use DB;
class AbortIfNotOwner
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string $resourceName
* #return mixed
*/
public function handle($request, Closure $next, $resourceName)
{
$resourceId = $request->route()->parameter($resourceName);
$user_id = \DB::table($resourceName)->find($resourceId)->user_id;
if ($request->user()->id != $user_id) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
}
I register it in the app\Http\Kernel.php
protected $routeMiddleware = [
'owner' => 'App\Http\Middleware\AbortIfNotOwner',
];
and in my route file I have:
Route::group(['middleware' => ['owner:bids']], function() {
Route::get('user/{id}/bids', ['as' => 'buyer_bids', 'uses' => 'User\Buyer\BidsController#getBidsPerUser']);
});
When I run this code I get an
ErrorException in AbortIfNotOwner.php line 23: Trying to get property
of non-object
This refers to the following lines in the middleware:
> $resourceId = $request->route()->parameter($resourceName);
> $user_id = \DB::table($resourceName)->find($resourceId)->user_id;
The issue seems to be that resourceId is null I think. I do have a field user_id in the bids table, so am not sure what is wrong. The route URL is like /user/2/bids.
EDIT - SOLVED
I found that the below works:
$user_id = \DB::table($resourceName)->find($request->id)->user_id;
instead of
$resourceId = $request->route()->parameter($resourceName);
$user_id = \DB::table($resourceName)->find($resourceId)->user_id;
This works with the routes like
Route::get('/{id}/bids'
EDIT - SOLVED - Other solution
$resourceId = $request->route()->parameter($resourceName);
$user_id = \DB::table($resourceName)->find($resourceId)->user_id;
will work if the route is changed to
Route::get('/{bids}/bids'
... instead of
Route::get('/{id}/bids'...

Change your route like this Route::get('user/{bids}/bids'... becasse to get a route parameter like this $request->route()->parameter('name') it has to match the parameter name in the route, meaning the bolded one user/{bids}/bids.

\DB::table($resourceName)->find($resourceId) is returning null (no results), so there's no user_id property to it. Check that you've found a result before attempting to access its properties.
Same thing for $request->user()->id - if $request->user() is null due to the user not being logged in, it'll fail.

Related

Segments are not getting shifted / Cannot get correct arguments in Controller

I am trying to implement a simple localization for an existing Laravel project.
Implementing Localization based on the following tutorial:
https://laraveldaily.com/multi-language-routes-and-locales-with-auth/
Here is the simplified code before localization implementation:
web.php
Route::get('/poll/{poll_id}', 'App\Http\Controllers\PollsController#view');
PollsController#view
public function view($poll_id){
echo "poll_id: ".$poll_id;
}
TEST
URL: http://domain.name/poll/1
RESULT: poll_id: 1
Here are the simplified changes required for localization and the result I get:
web.php
Route::group(['prefix' => '{locale}', 'where' => ['locale' => '[a-zA-Z]{2}'], 'middleware' => 'setlocale'], function() {
Route::get('/poll/{poll_id}', 'App\Http\Controllers\PollsController#view');
});
Middleware/SetLocale
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class SetLocale
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next){
app()->setLocale($request->segment(1));
return $next($request);
}
}
PollsController#view remained unchanged.
Now, when I open the following URL (http://domain.name/en/poll/1), the result is:
RESULT: poll_id: en
QUESTION
Is there a way to ignore "'prefix' => '{locale}'" in controller or get arguments somehow shifted so that in the controller I still get poll_id=1, not locale=en?
PS. The easiest fix would be to add another argument to PollsController#view in the following way, but it does not smell well and then I would need to add locale argument to all functions, although I do not use it there:
public function view($locale, $poll_id){
echo "poll_id: ".$poll_id;
}
In your middleware you can tell the Route instance to forget that route parameter so it won't try to pass it to route actions:
public function handle($request, $next)
{
app()->setLocale($request->route('locale'));
// forget the 'locale' parameter
$request->route()->forgetParameter('locale');
return $next($request);
}

Laravel 6 - How to Log All URL's of User Visited

I want to log all URL's that user visited. But there's not the url i want to logged from my code. Here are the code that im done, please give me some advise. Thanks and appreciate.
Web Routes
Route::get('/{url}', 'LogController#myTestAddToLog')->where('url', '[\w\d\-]+(.*)');
Log Controller
public function myTestAddToLog()
{
\LogActivity::addToLog('My Testing Add To Log.');
}
App/Helpers LogActivity
public static function addToLog($subject)
{
$log = [];
$log['url'] = Request::fullUrl();
$log['ip'] = Request::ip();
$log['user_id'] = auth()->check() ? auth()->user()->id : 1;
LogActivityModel::create($log);
}
You need to create a middleware.
php artisan make:middleware AddToLog
And then, put your code inside the middleware.
<?php
namespace App\Http\Middleware;
use Closure;
use App\LogActivityModel;
class AddToLog
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
if(auth()->user()) {
LogActivityModel::create([
'url' => request()->fullUrl(),
'ip' => request()->ip(),
'user_id' => auth()->id(),
]);
}
return $response;
}
}
Then, inside app/Http/kernel.php, add your middleware to $middleware. This would allow any URL called would trigger this code.
protected $middleware = [
...
\App\Http\Middleware\AddToLog::class,
];
For those who still has the issue I had modified a bit the answer provided by #Adlan and based on the comments of his answer. Since you will be logging the activities after the request has been performed, the accepted answer needs to be modified. Following is the modified code.
public function handle($request, Closure $next)
{
$response = $next($request);
if(auth()->user()) {
Activity::create([
'url' => request()->fullUrl(),
'ip_address' => request()->ip(),
'user_id' => auth()->id(),
]);
}
return $response;
}
First, the response for the next request is saved. Then user authentication is checked and finally, the response is returned.
Reference : https://laravel.com/docs/5.8/middleware
https://stackoverflow.com/a/59816949/7070809
Please use the below code to log all url that user has visited
Log::info(url()->full()); // For just loggin
And for storing user log create table store the url with timestamp and IP

Laravel multiple parameters in Route middelware not working

I have problem that using multiple parameters in my Route::middleware isn't working for me. I am trying to assign a specific route only accessible for a superuser and admin role.
When I just use:
role:superuser
it works fine, but when I add a second parameter like:
role:superuser,admin
it fails when I assign myself the admin role but still works for the superuser role.
I am confused so any help would be appreciated!
Here is my RoleMiddleware:
namespace App\Http\Middleware;
use Closure;
class RoleMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string $roles
* #return mixed
*/
public function handle($request, Closure $next, ...$roles)
{
$user = $request->user();
if ($user && $user->isSuperuser($roles)) {
return $next($request);
}
return redirect('/home')->withError('U heeft niet de juiste rechten!');
}
}
Here is my isSuperuser method in my User model:
public function isSuperuser(...$roles)
{
if ($roles) {
return $this->roles == $roles;
}
return $this->roles;
}
Last but not least my routes/web code for the middleware:
Route::get('/users', 'UsersController#index')->middleware(['role:superuser,admin']);
Btw: the method is called 'isSuperuser' but that's just a name. It also has to accept the admin role at some point.
use | instead of , like this:
Route::get('/users', 'UsersController#index')->middleware(['role:superuser|admin']);

Pass parameter to Laravel Middleware

How can I passed a parameter in my middleware? I'm always getting this error
Here are the structure of my middlware
<?php
namespace App\Http\Middleware;
use Closure;
class SubDomainAccess
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $subdomain)
{
dd($subdomain); // Just trying to output the result here
return $next($request);
}
}
And on the Kernel.php under the $routeMiddleware I added this
'subdomain.access' => \App\Http\Middleware\SubDomainAccess::class,
Now on my web.php route file I added this
Route::group(['domain' => '{subdomain}.' . config('site.domain')], function () {
Route::get('/', ['as' => 'site.home', 'uses' => 'Site\Listing\ListingController#showListing'])->middleware('subdomain.access');
});
Also I tried this
Route::group(['domain' => '{subdomain}.' . config('site.domain')], function () {
Route::group(['middleware' => 'subdomain.access'], function () {
Route::get('/', ['as' => 'site.home', 'uses' => 'Site\Listing\ListingController#showListing']);
});
});
I tried this but nothings working. The only thing I haven't tried is placing the middleware in my controller constructor. But I don't wan't it that way as I think this is messy and it's more elegant if its within the route file.
Hope you can help me on this. Thanks
Ok so I managed to find a way to get the parameters without passing a third parameter on the middleware handle function thanks to this link
So what I did to retrieve the subdomain parameter is this
$request->route()->parameter('subdomain')
or if all parameter
$request->route()->parameters()
['middleware' => 'subdomain.access'] is wrong, try to use ['middleware' => 'subdomain:access'] with a : instead.
https://mattstauffer.co/blog/passing-parameters-to-middleware-in-laravel-5.1
Get URI from $request object and then return domain. No need to pass subdomain as params to middleware.
namespace App\Http\Middleware;
use Closure;
class SubDomainAccess
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $subdomain)
{
$sudomain = $this->getSubDomain($_SERVER['HTTP_HOST']);
return $next($request);
}
/**
* Get Subdomain name
* #param $uri
* #return bool
*/
private function getSubDomain($uri)
{
if(!empty($uri))
{
$host = explode('.', $uri);
if(sizeof($host) > 2)
return $host[0];
}
return false;
}
}

Laravel middleware 'except' rule not working

I have a controller with the following in the constructor:
$this->middleware('guest', ['except' =>
[
'logout',
'auth/facebook',
'auth/facebook/callback',
'auth/facebook/unlink'
]
]);
The 'logout' rule (which is there by default) works perfectly but the other 3 rules I have added are ignored. The routes in routes.php look like this:
Route::group(['middleware' => ['web']],function(){
Route::auth();
// Facebook auth
Route::get('/auth/facebook', 'Auth\AuthController#redirectToFacebook')->name('facebook_auth');
Route::get('/auth/facebook/callback', 'Auth\AuthController#handleFacebookCallback')->name('facebook_callback');
Route::get('/auth/facebook/unlink', 'Auth\AuthController#handleFacebookUnlink')->name('facebook_unlink');
}
If I visit auth/facebook, auth/facebook/callback or auth/facebook/unlink whilst logged in I get denied by the middleware and thrown back to the homepage.
I've tried specifying the 'except' rules with proceeding /'s so they match the routes in routes.php exactly but it makes no difference. Any ideas why these rules are being ignored, whilst the default 'logout' rule is respected?
Cheers!
You need to pass the method's name instead of the URI.
<?php
namespace App\Http\Controllers;
class MyController extends Controller {
public function __construct() {
$this->middleware('guest', ['except' => [
'redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink'
]]);
}
}
Since Laravel 5.3, you can use fluent interface to define middlewares on controllers, which seems cleaner than using multidimensional arrays.
<?php
$this->middleware('guest')->except('redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink');
I solved this issue in my Middleware by adding this inExceptArray function. It's the same way VerifyCsrfToken handles the except array.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class MyMiddleware
{
/**
* Routes that should skip handle.
*
* #var array
*/
protected $except = [
'/some/route',
];
/**
* Determine if the request has a URI that should pass through.
*
* #param Request $request
* #return bool
*/
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->is($except)) {
return true;
}
}
return false;
}
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// check user authed or API Key
if (!$this->inExceptArray($request)) {
// Process middleware checks and return if failed...
if (true) {
// Middleware failed, send back response
return response()->json([
'error' => true,
'Message' => 'Failed Middleware check'
]);
}
}
// Middleware passed or in Except array
return $next($request);
}
}
If you are trying to follow the Laravel Documentation, an alternative solution to this is suggested by adding routes to the $except variable in the /Http/Middleware/VerifyCsrfToken.php file. The documentation says to add them like this:
'route/*'
But I found the only way to get it to work is by putting the routes to ignore like this:
'/route'
When assigning middleware to a group of routes, you may occasionally need to prevent the middleware from being applied to an individual route within the group. You may accomplish this using the withoutMiddleware method:
use App\Http\Middleware\CheckAge;
Route::middleware([CheckAge::class])->group(function () {
Route::get('/', function () {
//
});
Route::get('admin/profile', function () {
//
})->withoutMiddleware([CheckAge::class]);
});
for more information read documentation laravel middleware
Use this function in your Controller:
public function __construct()
{
$this->middleware(['auth' => 'verified'])->except("page_name_1", "page_name_2", "page_name_3");
}
*replace page_name_1/2/3 with yours.
For me it's working fine.
I have this solved, and here's what I am doing. Aso, I just realized this is very similar to what cmac did in his answer.
api.php
Route::group(['middleware' => 'auth'], function () {
Route::get('/user', 'Auth\UserController#me')->name('me');
Route::post('logout', 'Auth\LoginController#logout')->name('logout');
});
LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers, ThrottlesLogins;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
// ...
/**
* If the user's session is expired, the auth token is already invalidated,
* so we just return success to the client.
*
* This solves the edge case where the user clicks the Logout button as their first
* interaction in a stale session, and allows a clean redirect to the login page.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$user = $this->guard()->user();
if ($user) {
$this->guard()->logout();
JWTAuth::invalidate();
}
return response()->json(['success' => 'Logged out.'], 200);
}
}
Authenticate.php
class Authenticate extends Middleware
{
/**
* Exclude these routes from authentication check.
*
* Note: `$request->is('api/fragment*')` https://laravel.com/docs/7.x/requests
*
* #var array
*/
protected $except = [
'api/logout',
];
/**
* Ensure the user is authenticated.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
foreach ($this->except as $excluded_route) {
if ($request->path() === $excluded_route) {
\Log::debug("Skipping $excluded_route from auth check...");
return $next($request);
}
}
// code below here requires 'auth'
{ catch ($e) {
// ...
}
}
I over-engineered it slightly. Today I only need an exemption on /api/logout, but I set the logic up to quickly add more routes. If you research the VerifyCsrfToken middleware, you'll see it takes a form like this:
protected $except = [
'api/logout',
'api/foobars*',
'stripe/poop',
'https://www.external.com/yolo',
];
That's why I put that "note" in my doc above there. $request->path() === $excluded_route will probably not match api/foobars*, but $request->is('api/foobars*') should. Additionally, a person might be able to use something like $request->url() === $excluded_route to match http://www.external.com/yolo.
You should pass the function name to 'except'.
Here's an example from one of my projects:
$this->middleware('IsAdminOrSupport', ['except' => [
'ProductsByShopPage'
]
]);
This means the middleware 'IsAdminOrSupport' is applied to all methods of this controller except for the method 'ProductByShopPage'.

Resources