Laravel 5 Cashier Middleware Routing Error - php

I've implemented the cashier / billing feature from Laravel 5 and I'm trying to protect a group of routes using middleware which checks for a subscription.
I'm getting the following error:
Argument 2 passed to App\Http\Middleware\HasSubscription::handle() must be an instance of App\Http\Middleware\Closure, instance of Closure given
Heres my Middleware
<?php
namespace App\Http\Middleware;
class HasSubscription
{
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed()) {
// This user is not a paying customer...
return redirect('subscription');
}
return $next($request);
}
}
Heres my protected route
Route::get('home', 'PagesController#index')->middleware('subscription');
Heres my applications route declaration
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'subscription' => \App\Http\Middleware\HasSubscription::class,
];
Any idea why I get the error at the top?

Just add
use Closure;
to the top of your middleware, just before class definition:
namespace App\Http\Middleware;
use Closure;
class HasSubscription
{
...
Take a look on the the example in manual: http://laravel.com/docs/5.1/middleware#defining-middleware

Related

Laravel Parameter in every url

I have read almost everything in web and documentation but i can't find solution for my Problem.
I have a variable stored in Session , then I want to put this variable in every url generated by route('some-route') .
In Session I have sub = "mysubid"
When I generate Route route('my-route') I want to pass this sub parameter in query string: http://domain.dom/my-route-parameter?sub=mysubid
Can you help me to solve This problem? Any helpful answer will be appreciated;
You can use the Default Values feature.
First create a new middleware php artisan make:middleware SetSubIdFromSession. Then do the following:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetSubIdFromSession
{
public function handle($request, Closure $next)
{
URL::defaults(['sub' => \Session::get('sub')]);
return $next($request);
}
}
At the end register your new middleware in app/Http/Kernel.php by adding it to $routeMiddleware.
protected $routeMiddleware = [
// other Middlewares
'sessionDefaultValue' => App\Http\Middleware\SetSubIdFromSession::class,
];
Add {sub} and the middleware to your route definition:
Route::get('/{sub}/path', function () {
//
})
->name('my-route')
->middleware('sessionDefaultValue');
Since you want this on every web route you can also add the middleware to the web middleware group:
protected $middlewareGroups = [
'web' => [
// other Middlewares
'sessionDefaultValue',
],
'api' => [
//
]
];
Try this , You need to create middleware php artisan make:middleware SetSubSession
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetSubsSession
{
public function handle($request, Closure $next)
{
if(session('sub')){
$url = url()->full();
return redirect($url.'?sub='.session('sub'));
}
return $next($request);
}
}
in app/http/Kernel.php
protected $routeMiddleware = [
........
'setsubsession' => \App\Http\Middleware\SetSubsSession::class,
]
in route.php add
Route::group(['middleware' => 'setsubsession'], function(){
//and define all the route you want to add sub parameter
});
using this you don't need to change all your routes.This will automatic add "sub" in the route define in that middleware.

Laravel middleware terminate not called when using constructor version

Laravel version: 5.1.46
routes.php
Route::get('/rocha', 'RochaController#index');
Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'Age' => \App\Http\Middleware\AgeMiddleware::class,
'Role' => \App\Http\Middleware\RoleMiddleware::class,
];
RochaController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class RochaController extends Controller
{
public function __construct() {
$this->middleware('Role');
}
public function index() {
echo '<br>Hi I am index';
}
}
RochaMiddleware.php
namespace App\Http\Middleware;
use Closure;
class RoleMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
echo '<br>Hi I am middleware';
return $next($request);
}
public function terminate($request, $response) {
echo '<br>Shtting down...';
}
}
Result:
Hi I am middleware
Hi I am index
When I use middleware inside the controller via its constructor $this->middleware('Role') the terminate() function does not get called. When I switch the code taking out the constructor in the controller and change the route to the following the terminate() function gets called:
Route::get('/rocha', [
'middleware' => 'Role',
'uses' => 'RochaController#index'
]);
Result:
Hi I am middleware
Hi I am index
Shtting down...
Why does the constructor version ($this->middleware('Role')) prevent the terminate() function from being called?
Why does the route version work and the terminate() function is called as opposed to the above?
If you define a terminate method on your middleware, it will automatically be called after the response is ready to be sent to the browser.
from terminable-middleware
I think you misunderstand the usage of terminate method. Laravel actually call the terminate method, but the browser will not show the output of terminate. Because the response has been sent to browsers.
You can use this terminate method to test whether the call is successful.
public function terminate($request, $response)
{
file_put_contents(__DIR__ . '/1.txt', 'hello terminate');
}
By the way, I'm test your code, it always output:
Hi I am middleware
Hi I am index
I also wonder why you can get Shtting down...

Laravel Auth Middleware: Class can does not exist

I'm trying to protect a route via middleware as described in the doc
When I hit the url, I get:
ReflectionException in Container.php line 749:
Class can does not exist
Here's the relevant part from routes.php:
Route::get('{user}/profile/edit/{type?}', [
'as' => 'edit',
'uses' => 'User\UserController#edit',
'middleware' => ['can:edit-user,user'],
]);
AuthServiceProvider.php:
public function boot()
{
$this->registerPolicies();
// ... other definitions
Gate::define('edit-user', function ($user, $subjectUser) {
return
$user->hasRole('manage.user') // if the user has this role, green
||
($user->isAdmin && $user->id == $subjectUser->id) // otherwise if is admin, can edit her own profile
;
});
Maybe it's because I'm not using a separate policy class for defining the gate?
According to the documentation on using Middleware with Routes - you need to register the definition in app/Http/Kernel.php
If you want a middleware to run during every HTTP request to your application, simply list the middleware class in the $middleware property of your app/Http/Kernel.php class.
The error you're seeing shows that this definition is missing. You need to add something like;
// Within App\Http\Kernel Class...
protected $routeMiddleware = [
//...
'can' => \Path\To\Your\Middleware::class,
];

Different users, middleware Laravel 5

I'm trying to use different user levels at my system. I have at data base my users and their levels (master, portais, chaves, etc)
So, what I want is different routes for each user, so each one will access just a part of the system.
I can easily check in each page of the system his AUTH and dont show to him the page if he is logged in a account witch he shouldn't see that page. BUT, there's a easy way to do it with middleware / Routes no?!
I tried something like this on my routes.php:
Route::group(['middleware' => 'Master'], function()
{
Route::any('/vendedor/produtos/removeProduto', 'AjaxController#vendedorRemoveProduto');
Route::any('/vendedor/produtos/editaPrecoProduto', 'AjaxController#editaPrecoProduto');
Route::any('/vendedor/produtos/alterarestadoproduto', 'AjaxController#alterarestadoumproduto');
Route::any('/vendedor/produtos/listaProdutos', 'AjaxController#listaProdutos');
Route::any('/vendedor/produtos/adicionaProdutoCliente', 'AjaxController#adicionaProdutoCliente');
Route::any('/', 'DashboardController#home'); });
Route::group(['middleware' => 'portais'], function()
{
Route::any('/cadastrarobjedu/criartema', 'AjaxController#criartemaobjedu');
Route::any('/cadastrarobjedu/cadastrar', 'AjaxController#cadastraobjedu');
Route::any('/cadastrarobjedu', 'DashboardController#cadastrarobjedu');
Route::any('/listarobjedu', 'DashboardController#listarobjedu');
Route::any('/editarobjeto/{id}', 'DashboardController#editarobjeto');
Route::any('/apagarobjeto/{id}', 'AjaxController#apagarobjeto');
Route::any('/', 'DashboardController#home'); });
But this didn't work, hgive me a error saying the "MASTER" class dosen't exist. I have change anything more?
You need to implement a middleware that would check if current user has given level.
Laravel 5.0
//app/Http/Kernel.php - register middleware classes
protected $routeMiddleware = [
'levelMaster' => 'App\Http\Middleware\LevelMasterMiddleware',
'levelPortais' => 'App\Http\Middleware\LevelPortaisMiddleware'
];
//app/Http/Middleware/LevelMiddleware.php - base middleware class that checks if user has level stored in $level;
namespace App\Http\Middleware;
use Closure;
use App;
use Auth;
abstract class LevelMiddleware
{
protected $level;
public function handle($request, Closure $next)
{
if (Auth::user() && Auth::user()->level !== $this->level) {
return App::abort(Auth::check() ? 403 : 401, Auth::check() ? 'Forbidden' : 'Unauthorized');
}
return $next($request);
}
}
//app/Kernel/Middleware/LevelMasterMiddleware.php - checks if user has level Master
namespace App\Http\Middleware;
class LevelMasterMiddleware extends LevelMiddleware
{
protected $level = 'master';
}
//app/Kernel/Middleware/LevelPortaisMiddleware.php - checks if user has level Portais
namespace App\Http\Middleware;
class LevelPortaisMiddleware extends LevelMiddleware
{
protected $level = 'portais';
}
//config/routes.php - configure routes for different levels
Route::group(['middleware' => 'levelMaster'], function()
{
//here add routes for users with level=master
}
Route::group(['middleware' => 'levelPortais'], function()
{
//here add routes for users with level=portais
}
Laravel 5.1
The latest version of Laravel introduces middleware parameters which lets simplify the above code a bit, as only one middleware class will be needed:
//app/Http/Kernel.php - register middleware class
protected $routeMiddleware = ['level' => 'App\Http\Middleware\LevelMiddleware'];
//app/Http/Middleware/LevelMiddleware.php - check if current user has given level
namespace App\Http\Middleware;
use Closure;
use App;
use Auth;
class LevelMiddleware
{
public function handle($request, Closure $next, $level)
{
if (Auth::user() && Auth::user()->level !== $level) {
return App::abort(Auth::check() ? 403 : 401, Auth::check() ? 'Forbidden' : 'Unauthorized');
}
return $next($request);
}
}
//config/routes.php - configure routes for different levels and pass level as middleware parameter
Route::group(['middleware' => 'level:master'], function()
{
//here add routes for users with level=master
}
Route::group(['middleware' => 'level:portais'], function()
{
//here add routes for users with level=portais
}

Roles with laravel 5, how to allow only admin access to some root

I follow this tutorial : https://www.youtube.com/watch?v=kmJYVhG6UzM Currently I can check in my blade if user is a admin or not like this:
{{ Auth::user()->roles->toArray()[0]['role'] }}
HI ADMIN
#endif
How can I make my route only available for admin user?
You need to create a middleware for your route.
Use: php artisan make:middleware AdminMiddleware.
You will find in your middleware folder a new file with this name.
Put your logic in your middleware, e.g.
public function handle($request, Closure $next)
{
if(Auth::check())
{
return $next($request);
}
else
{
return view('auth.login')->withErrors('You are not logged in');
}
}
Once you have done your logic in your middleware, you can either call it in the route or make the middleware apply to all routes.
If you want to add it to all routes, go to Kernel.php and add it to the $middleware array, e.g.
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'App\Http\Middleware\VerifyCsrfToken',
'App\Http\Middleware\AdminMiddleware',
];
If you want to add it to specific routes only, add it to the $routeMiddleware variable and add the alias to the route. E.g.
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'admin' => 'App\Http\Middleware\AdminMiddleware',
];
You can then add it to a route, as a filter, e.g.
Route::get('admin/profile', ['middleware' => 'admin', function()
{
}]);
For additional info visit the docs:
http://laravel.com/docs/master/middleware
EDIT
An improvement on this would be to use variadic functions which was introduced in PHP 5.6
http://php.net/manual/en/migration56.new-features.php
Instead of having to make a middleware for each permission set you can do the following
PermissionMiddleware
namespace App\Http\Middleware;
use Closure;
use \App\Models\Role;
class PermissionMiddleware
{
// Pass parameters to this middleware
public function handle($request, Closure $next, ...$permitted_roles)
{
//Get a users role
$role = new Role;
$role_name = $role->getUserRoleByName();
foreach($permitted_roles as $permitted_role) {
if($permitted_role == $role_name) {
return $next($request);
}
}
return redirect()->back()->withErrors('You do not have the required permission');
}
}
Notice the ...$permitted_roles
Route::get('admin/profile', ['middleware' => 'PermissionMiddleware:Admin,Marketing', function()
{
}]);
You can now specify as many roles as required for one middleware rather than creating multiple by using middleware parameters
Docs
https://laravel.com/docs/5.3/middleware#middleware-parameters
Let's assume you have a column in your users table with isAdmin name which has a default value of 0 (false)
You can give special access using middleware in laravel like you give access to logged in users using auth middleware in laravel.
Now you need to create a middleware using the command :
php artisan make:middleware AdminMiddleware
In your Kernel.php you need to add this line to protected $routeMiddleware
'admin' => \App\Http\Middleware\AdminMiddleware::class,
In your middleware folder you have the AdminMiddleware file.
In that you need to put your logic
In this case this is how it might look like depending upon you
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RoleMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::user()->isAdmin == '1') // is an admin
{
return $next($request); // pass the admin
}
return redirect('/'); // not admin. redirect whereever you like
}
}
Now in your route you have to pass the url using this middleware
Here is how it might look like
Route::get('/iamanadmin', ['middleware' => 'admin', function() {
return view('iamanadmin');
}]);
use middleware and check for admin user.
Route::get('admin', ['middleware' => 'checkadmin', function()
{
}]);
now create middleware and validate admin user.

Categories