Laravel 5.2 custom middleware crashes localhost page - laravel

I have created a middleware to check Authenticated user's Database and restrict them accessing routes. But when i apply the middleware to the routes the localhost page crashes.
The localhost page isn’t working
localhost redirected you too many times.
public function handle($request, Closure $next)
{
$user = \Auth::user();
if (($user->plan == 'ip') && ($user->balance >= 1299)) {
return $next($request);
}
return redirect('no_balance');
}
This is my route
Route::group(['middleware'=>['auth','client','balance']],function(){
Route::get('/no_balance',[
'uses' => 'settingsController#noBalance',
'as' => 'no_balance',
]);
});
Balance is the middleware code i have included above and without balance middleware the application works fine. BUt when i include balance middleware it throws the error.
My controller code
public function noBalance(){
return view('no_balance')->with(compact('user_profiles','user_info'));
}

Your issue lies here:
Route::group(['middleware'=>['auth','client','balance']],function(){
Route::get('/no_balance',[
'uses' => 'settingsController#noBalance',
'as' => 'no_balance',
]);
});
Your balance middleware is checking if (($user->plan == 'ip') && ($user->balance >= 1299)) and if they fail this then you are redirecting them to 'no_balance' - which is also protected by this same middleware.
So you end up in a constant cycle of the middleware redirecting to 'no_balance' and then the middleware redirecting you again.
To fix this, just remove the 'no_balance' middleware from this route:
Route::group(['middleware'=>['auth','client']],function(){
Route::get('/no_balance',[
'uses' => 'settingsController#noBalance',
'as' => 'no_balance',
]);
});

I use this and it works. I hope it helps you. Instead of return redirect('no_balance'); I use this: abort(403,'Unauthorized action.');
which loads the 403 view in views/errors/403

Related

Laravel localization and routes from Jetstream / Fortify

I have this new Laravel project to work on. We would like to make it available in multiple languages.
I started the project with JetStream. Routes for authentication and such are automatically handled by JetStream / Fortify. I then added https://github.com/mcamara/laravel-localization to handle the localization. it works fine for the routes I created myself :
Route::group(
[
'prefix' => LaravelLocalization::setLocale(),
'middleware' => [ 'localeSessionRedirect', 'localizationRedirect', 'localeViewPath' ]
], function()
{
Route::get('/', function () {
return view('welcome');
});
Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
});
But how can I set the group, prefix and middleware on the routes handled by Jetstream and Fortify?
[EDIT]
So after some suggestions from #TEFO, I'm trying to add a middleware to handle setting the locale. Added :
Fortify.php :
'path' => '{lang}',
'middleware' => ['web', 'setLang']
new middleware setLang :
class SetLang {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(\Illuminate\Http\Request $request, Closure $next) {
// $lang = 'en';
// $request->attributes->add(['lang' => 'en']);
$request->route()->setParameter('lang', 'en');
// $request->request->set('lang', 'en');
return $next($request);
}
}
Added the middleware to $routeMiddleware.
I'm receiving this error when trying to reach http://mylaravel/en/login :
ErrorException
Missing required parameters for [Route: login] [URI: {lang}/login]. (View: /var/www/resources/views/auth/login.blade.php)
Finally successfully nailed this. I simply disabled routes from Fortify and Jetstream, copied them over and shoved them inside my grouped prefix routes. Still using https://github.com/mcamara/laravel-localization but it should work anyway you want it - make your own system or whatever, as long as you control the routes you're good to go.
In JetstreamServiceProvider :
public function register() {
Jetstream::ignoreRoutes();
}
In FortifyServiceProvider :
public function register() {
Fortify::ignoreRoutes();
}
And copy over routes from Fortify vendor/laravel/fortify/routes/routes.php and Jetstream vendor/laravel/jetstream/routes/livewire.php (I guess adapt to Inertia if you're working with this) over to your web.php file, inside a route group with the prefix you need.
I faced almost the same problem with the expection that i do not use mcamara/laravel-localization at the moment.
Based on the useful discussion above between #JeremyBelolo and #TEFO, the following solution worked for me:
Added 'path' => '{locale}/my-secret-path' to config/fortify.php. As #JeremyBelolo and #ETO discussed, the support for that was recenlty added.
Added my middleware before \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class to the web $middlewareGroups
Where my middleware set the locale app()->setLocale($locale); and the default {locale} url parameter URL::defaults(['locale' => $locale]); before passing the request deeper into the application.
Considering Jetstream I had to apply the same steps as #JeremyBelolo did, exept I didn't copy the jetsream/livewire routes but used the following inside the route group:
require base_path('vendor/laravel/jetstream/routes/livewire.php');
Now I can access {locale}/my-secret-path/login where {locale} is a supported locale for my site.
UPDATE [Fortify config option changed]:
The path fortify config option changed to prefix. Thus in config/fortify.php the following key should be used:
'prefix' => '{locale}/my-secret-path'
I made a new Laravel Project using Jetstream. I wanted to use multi-language support in my project, but when I used Prefix (en/login, de/login) according to languages in url, I was also having a problem with Route. I solved my problem by following these steps. I hope you will be useful too:
1 - I have included the package on this https://github.com/mcamara/laravel-localization in my project. and followed the instructions sequentially.
2 - I made the Route settings in the "rautes\web.php" file as follows.
Route::group(['prefix' => LaravelLocalization::setLocale(),'middleware' => [
'localeSessionRedirect', 'localizationRedirect','localeViewPath' ]], function(){
/** ADD ALL LOCALIZED ROUTES INSIDE THIS GROUP **/
Route::get('/', function () {return view('welcome');});
Route::middleware(['auth', 'verified'])->get('/dashboard', function () {
return view('back.dashboard');})->name('dashboard');
});
3 - I have included the in app\Http\Middleware\Kernel.php. In middlewareGroups end of web prefix.
protected $middlewareGroups = [
'web' => [....
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
\Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
\Mcamara\LaravelLocalization\Middleware\LocaleCookieRedirect::class,
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationViewPath::class,]
4 - Fortify Routes, include prefix in vendor\laravel\fortify\routes.php - Route::group like this:
Route::group(['prefix' => LaravelLocalization::setLocale(),
'middleware' => config('fortify.middleware', ['web'])], function () {
$enableViews = config('fortify.views', true);
.......
5 - Livewire Routes, include prefix in vendor\laravel\jetstream\routes\livewire.php - Route::group like this:
Route::group(['prefix' => LaravelLocalization::setLocale(),
'middleware' =>config('jetstream.middleware', ['web'])], function () {
if (Jetstream::hasTermsAndPrivacyPolicyFeature()) {
Route::get('/terms-of-service', [TermsOfServiceController::class, 'show'])-
>name('terms.show');
Route::get('/privacy-policy', [PrivacyPolicyController::class, 'show'])-
>name('policy.show');}
6 - If you want to separate backend and frontend, you can add in app\Http\Middleware\Kernel.php end of protected $routeMiddleware with prefix like in this https://github.com/mcamara/laravel-localization.
protected $routeMiddleware = [
........
'localize'=> \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
'localizationRedirect' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
'localeSessionRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
'localeCookieRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleCookieRedirect::class,
'localeViewPath' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationViewPath::class,
]
7 - And the happy end...

Router redirecting to the another page

I have route like
Route::get('admin/selfcontacteditdata','SelfcontectController#edit')->name('selfcontectedit');
Route::post('admin/selfcontactupdatedata','SelfcontectController#update')->name('selfcontectupdate');
If i just go to my browser and right admin/selfcontacteditdata it redirect me to
admin/newsshowdata
And my index function is
public function __construct()
{
return $this->middleware('auth');
}
public function index()
{
request()->validate([
'email' => 'required',
'mobileno' => 'required',
'facebook'=>'required',
'google'=>'required',
'map'=>'required',
]);
$data = selfcontect::find(1);
return view('/admin/selfcontectedit',compact('data'));
}
And my middleware is
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
My rest admin routes are working fine.
I had the same problem but I was writing table name wrong and my file was not saved as .blade please check are you also doing the same thing and there is no meaning of validation in edit function your edit function must be like
public function edit()
{
$data = selfcontect::find(1);
return view('/admin/selfcontectedit',compact('data'));
}
and your function name should be edit
You should use Accept key not Content/type
You can't redirect through view, actually your are calling view.
Correct syntax is
return view('view_name',compact('data'));
If you want to redirect to any route you have to call like this
return redirect()->to('admin/selfcontacteditdata');
Redirect to a Route
If in your routes.php file you have a route with a name, you can redirect a user to this particular route, whatever its URL is:
app/Http/routes.php:
get('books', ['as' => 'books_list', 'uses' => 'BooksController#index']);
app/Http/Controllers/SomeController.php
return redirect()->route('books');
This is really useful if in the future you want to change the URL structure – all you would need to change is routes.php (for example, get(‘books’, … to get(‘books_list’, …), and all the redirects would refer to that route and therefore would change automatically.
And you can also use parameters for the routes, if you have any:
app/Http/routes.php:
get('book/{id}', ['as' => 'book_view', 'uses' => 'BooksController#show']);
app/Http/Controllers/SomeController.php
return redirect()->route('book_view', 1);
In case of more parameters – you can use an array:
app/Http/routes.php:
get('book/{category}/{id}', ['as' => 'book_view', 'uses' =>
'BooksController#show']);
app/Http/Controllers/SomeController.php
return redirect()->route('book_view', [513, 1]);
Or you can specify names of the parameters:
return redirect()->route('book_view', ['category'=>513, 'id'=>1]);

Localization in laravel

I designed a site with Laravel. now I want add new language to it.I read laravel document . It was good but I have a problem.suppose I have a page that show detail of products so I have a route like mysite.com/product/id that get product's id and show it.also I have a method in controller like
public function showProduct($id){
...
}
If I add new Language , the route will change to this: mysite/en/product/id
now I must change my method because now two parameter send my method.something like this :
public function showProduct($lang,$id){
...
}
So two problems arise:
I must change all method in my site which is time consuming
I do not need language parameter in methods because I set $locan via middleware
pay attention that I do not want remove for example en from my URL (because of SEO)
Open your RouteServiceProvider and say that language parameter actually is not a parameter, it's a global prefix.
protected function mapWebRoutes()
{
Route::group([
'middleware' => 'web',
'namespace' => $this->namespace,
'prefix' => Request::segment(1) // but also you need a middleware about that for making controls..
], function ($router) {
require base_path('routes/web.php');
});
}
here is sample language middleware, but it's need to be improve
public function handle($request, Closure $next)
{
$langSegment = $request->segment(1);
// no need for admin side right ?
if ($langSegment === "admin")
return $next($request);
// if it's home page, get language but if it's not supported, then fallback locale gonna run
if (is_null($langSegment)) {
app()->setLocale($request->getPreferredLanguage((config("app.locales"))));
return $next($request);
}
// if first segment is language parameter then go on
if (strlen($langSegment) == 2)
return $next($request);
else
// if it's not, then you may want to add locale language parameter or you may want to abort 404
return redirect(url(config("app.locale") . "/" . implode($request->segments())));
}
So in your controller, or in your routes. you don't have deal with language parameter
Something like
Route::group(['prefix' => 'en'], function () {
App::setLocale('en');
//Same routes pointing to the same methods...
});
Or
Route::group(['prefix' => 'en', 'middleware' => 'yourMiddleware'], function () {
//Same routes pointing to the same methods...
});

Laravel: always adding user/{id} to routes or different method

I have a Laravel application. In the routes file, I have
Route::group(['prefix' => 'user'], function () {
Route::group(['middleware' => ['auth', 'roles'], 'roles' => ['buyer']], function() {
Route::get('dashboard/buyer', ['as' => 'buyer_dashboard', 'uses' => 'User\Buyer\DashboardController#index']);
});
Route::group(['middleware' => ['auth', 'roles'], 'roles' => ['seller']], function() {
Route::get('dashboard/seller', ['as' => 'seller_dashboard', 'uses' => 'User\Seller\DashboardController#index']);
});
});
I have a middleware that basically checks if the id as supplied in the route, is the same as the current logged in user. If this is not the case, I return an error page. The reason for having this is that I want to prevent that a user can access the dashboard of another user. I also want to prevent that a user can place a bid for someone else (by changing the id in the http request)
The issue is that in the first route, the id is referring to the user. In the second route, the id is referring to the lot id.
Am I obliged to change the second route to:
Route::get('{id}/lot/{lot}/bid/create', ['as' => 'buyer_lot_bid_create', 'uses' => 'User\Buyer\BidsController#create']);
where the first id refers to the user so that I can check the id of the user?
Or is there another way to prevent users from accessing other users pages without explicitly passing the user/{id} in the route?
Doing this in a middleware sounds like a bad idea. You've already come up against one exception to your middleware rule, and you'll certaily come across more.
There's two ways to do this:
Using laravel's built in authorisation tools: https://laravel.com/docs/5.1/authorization
If the check is that the "id as supplied in the route, is the same as the current logged in user" then there's no need to pass the user's id in via the route at all. The user can just visit eg. /dashboard/buyer with no params in the route. Who's dashboard are they visiting? The one of the logged in user, of course. So there's no way for a user to even try to visit another user's dashboard. Likewise with bidding. You can make your bid endpoint so that the bidder's id is not passed in via a route - it's just set to the id of the logged in user in your controller method. So again, there's no way to even try to bid on behalf of another user.
class AuthServiceProvider extends ServiceProvider
{
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$gate->define('see-profile', function ($user, $profile) {
return $user->id === $profile->user_id;
});
}
Controller:
public function profile($id)
{
$post = Profile::findOrFail($id);
if (Gate::denies('see-profile', $profile)) {
abort(403);
}
}

Laravel Conditional route filter

Hey guys could you please help me? This one is driving me crazy...
Let's say that I have a method for checking if the user is an admin or not:
public function isAdmin()
{
return Auth::user()->role === 'admin';
}
Then I attach it to a route filter:
Route::filter('admin', function($route, $request)
{
if ( ! Auth::user()->isAdmin()) {
Notification::error('No permission to view this page!');
return Redirect::back();
}
});
Now, I just pass it to the route group
Route::group(array('before' => 'admin'), function()
{
Route::post('users/{id}/update_password', 'UserController#update_password');
Route::post('users/{id}/delete', 'UserController#force_delete');
Route::delete('users/{id}', array('as' => 'users.destroy', 'uses' => 'UserController#destroy'));
Route::post('users/{id}/restore', 'UserController#restore');
Route::get('users/create', array('as' => 'users.create', 'uses' => 'UserController#create'));
Route::post('users', array('as' => 'users.store', 'uses' => 'UserController#store'));
Route::get('users/{id}/edit', array('as' => 'users.edit', 'uses' => 'UserController#edit'));
Route::put('users/{id}', array('as' => 'users.update', 'uses' => 'UserController#update'));
});
The question here is how do I allow a user to bypass this filter if for example he's trying to update it's own profile page an obviously he's not and admin?
I just want to block all access to the users routes for nonadmins but allow the user to edit/update etc on his own profile but allow the admin to do that too.
Could you please point me to the right direction?
You can get the related request segment to check it in your filter:
Route::filter('admin', function($route, $request)
{
if ( ! Auth::user()->isAdmin() && Auth::user()->username !== Request::segment(2)) {
Notification::error('No permission to view this page!');
return Redirect::back();
}
});
There are a few ways to do this, but having a filter that checks the request segments against the currently authenticated user isn't the best way.
Choice Number 1
You simply check that a user is auth'd (use the auth filter), and then in the controller itself you check whether or not the user is an admin, and/or it's their profile.
Choice Number 2
Define a secondary sets of routes specifically for a user modifying their own profile, that doesn't follow the /user/{id}/* pattern.
Route::group(['before' => 'admin'], function() {
// admin routes here
}
Route::group(['prefix' => '/me'], function() {
Route::post('/update_password', 'UserController#update_password');
Route::post('/delete', 'UserController#force_delete');
// etc
}
This would mean that to edit their own profile, they could simply go to /me/edit rather than /user/{id}/edit. To avoid issues like repeating the same code, or errors because an argument is missing, you could do something like this in your controller.
private function getUserOrMe($id)
{
return $id !== false ? User::find($id) : Auth::user();
}
public function edit($id = false)
{
$user = $this->getUserOrMe($id);
}
I recently used this particular method for an API. Sure it requires defining the routes again, but providing that you've set them up with groups that make use of the prefix option, it's a copy and paste job, plus, there are routes an admin would have that a user wouldn't.
Either way, filters weren't intended to do complex logic, but rather, to provide a level of base logic and protection for routes. Logic that identifies whether the current uri is that of the currently logged in user, is something better handled in a controller.

Resources