I'm just learning laravel and now i'm stuck at Middleware stuff. I have class AdultMiddleware.php :
<?php
namespace App\Http\Middleware;
use Closure;
class AdultMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Session::get('age') < 18) {
return view('search');
}
return $next($request);
}
}
and it's short name in Kernel.php :
protected $routeMiddleware = [
'adult' => \App\Http\Middleware\AdultMiddleware::class,
];
And in web.php :
Route::get('middle', array('as' => 'middle', 'age' => '16', 'before' => 'adult', function()
{
return view('welcome');
}));
As you see in routes I set age as 16 , but still it returns welcome instead of search. What I'm doing wrong? I know it may sound newbie, but I did try to google and had no luck. That's why I'm asking here.
Assigning middleware to a route:
Route::get(..., ['middleware' => 'adult', ...]);
Route::get(..., ...)->middleware('adult');
before is for filters which don't exist any more in Laravel since middleware replaced filters.
Route::get('middle', ['as' => 'middle', 'middleware' => 'adult', function () {
return view('welcome');
}]);
Laravel Docs - 5.2 - Middleware - Assigning Middleware to routes
Laravel Docs - 5.5 - Middleware - Assigning Middleware to routes
If your middleware is going to check a session variable, you could add a route to be able to set that session variable as you like.
Route::get('set/age/{age}', function ($age) {
session(['age' => $age]);
return redirect()->route('middle');
});
That will set the age variable in the session and redirect you to your 'middle' route.
Also you may want to return a redirect to the search page instead of returning a view from the middleware.
Related
On a Laravel API I've set rate limits using the default middleware for throttling;
Route::group(['prefix' => 'products'], function() {
Route::get('/', ['as' => 'products.index', 'uses' => 'CustomerProductController#index'])->middleware('throttle:60,1');
Route::get('/{product}', ['as' => 'products.show', 'uses' => 'CustomerProductController#show'])->middleware('throttle:50,1');
});
Now I need to make my own middleware to exclude 1 ip address from throttling.
But somehow I can only find suggestions on doing things the other way around eg. throttling a group of ip addresses.
Can someone give me a nudge in the right direction?
Here's a short overview of what I would do.
Step 1
Create a new middleware i.e. ThrottleRequestsWithIp
php artisan make:middleware ThrottleRequestsWithIp
Step 2
Let it extend the original throttle middleware class \Illuminate\Routing\Middleware\ThrottleRequests.
If you want to take a look at the original framework middleware you can find it under /vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php
Overwrite the handle method to check for the IP address and call the parent method if it's not found.
This is how your App\Http\Middleware\ThrottleRequestsWithIp could look like
<?php
namespace App\Http\Middleware;
use Closure;
class ThrottleRequestsWithIp extends \Illuminate\Routing\Middleware\ThrottleRequests
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1, $prefix = '')
{
if($request->ip() === "192.168.10.2")
return $next($request);
return parent::handle($request, $next, $maxAttempts, $decayMinutes, $prefix);
}
}
Step 3
Register your new middleware in Kernel.php, for example
'throttleIp' => \App\Http\Middleware\ThrottleRequestsWithIp::class
Step 4
Use it in your routes like this
Route::get('/', [
'as' => 'products.index',
'uses' => 'CustomerProductController#index'
])->middleware('throttleIp:60,1');
I try to Entrust in my Laravel code.
At this moment I have users, permisions and roles.
create admin panel where you want to access the "permissions" == "admin - panel"
I wish it was done by the file routes.php
My files:
Middleware/EntrustMiddleware.php
class EntrustMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!Entrust::can('admin-panel')) {
return Redirect::to('home');
}
return $next($request);
}
}
routes.php
Route::get('admin-panel', ['middleware' => ['auth', 'Entrust'], function () {
}]);
I have tried many methods , but still does not work. Can anyone suggest how to set file " routes.php " to access the " admin - panel / 'was only when "permissions" == "admin-panel"
--Edit--
When i'm using this method i get error:
Route::group(['middleware' => ['Entrust']], function () {
//put your routes here
Route::get('/admin', 'Admin\AdminController#index');
});
ErrorException in Pipeline.php line 136: call_user_func_array() expects parameter 1 to be a valid callback, class 'Zizaco\Entrust\EntrustFacade' does not have a method 'handle'
--edit2--
['middleware' => ['permission:NAME']]
Now I understand :)
Is Contoller I have to add some extra security or not?
If you are using entrust, for all those routes which you want to allow access for specific roles, you just need to put it in group, that will do the job, being said that, here's how it will look like,
Route::group(['middleware' => ['add roles name here']], function () {
//put your routes here
});
I'm actually implementing 2-factor auth into the project. What I did was
Auth::user()->google2fa_passed = 1;
In fact it doesn't really store, when navigate to another page, the value is missing.
I also don't want to keep in another session, because when user logout (or users remove the session cookie from their browser), then will show a login page, and go through the 2 factor auth again.
Any idea how to save 1 more attribute to user session?
When you use Auth::user() it give you the Eloquent model of the authenticate user.
If you want to store data in session you need to use the Session facade or session() helper.
You can find more information about session in the documentation.
PS: Old version of the documentation is better (http://laravel.com/docs/5.0/session).
Eventually, I use session to store.
After key in the 6-digits code, store a flag into session
\Session::put('totp_passed', 1);
In app/Http/Middleware/Authenticate.php, remove the 2FA session if session expired
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
// remove the 2-factor auth if the user session expired
\Session::forget('totp_passed'); // <------- add this line
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->route('auth.login');
}
}
return $next($request);
}
Then create another middleware, e.g. app/Http/Middleware/TwoFactorAuth.php
namespace App\Http\Middleware;
use Closure;
class TwoFactorAuth
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!\Session::has('totp_passed')) {
return redirect()->route('auth.2fa');
}
return $next($request);
}
}
In app/Http/Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'2fa' => \App\Http\Middleware\TwoFactorAuth::class, // <------ add this line
];
How to use
Route::group(['middleware' => 'auth'], function () {
// must be login first only can access this page
Route::get('2fa', ['as' => 'auth.2fa', 'uses' => 'Auth\AuthController#get2FactorAuthentication']);
Route::post('2fa', ['uses' => 'Auth\AuthController#post2FactorAuthentication']);
// add 2-factor auth middleware
Route::group(['middleware' => '2fa'], function () {
// all routes that required login
});
});
I've started learning Laravel 5.1 and so far I'm liking it! But there is one thing I don't get yet..
In my previous project I had 2 specific controllers (eg: "normal", "extended") which , after a successfull login, were called based on the Users user_group from the database.
If "Foo.Bar" enters his valid credentials and has the group normal he is redirected to NormalControler. Since I wasn't using any framework I restricted access to the other group by setting a $_SESSION with the group and checking it. So if another group tried to access that controller he got redirected.
How would this be achievable in Laravel 5? So far I have a controller which is callable without an Authentication and one restricted by this code in routes.php :
// All routes in the group are protected, only authed user are allowed to access them
Route::group(array('before' => 'auth'), function() {
// TO-DO : Seperate Controller access
});
And the login looks like this :
public function performLogin()
{
$logindata = array(
'username' => Input::get('user_name'),
'password' => Input::get('user_pass')
);
if( Auth::attempt( $logindata ) ){
// return \Redirect::to( check group and access this controller based on it);
}
else {
// TO-DO : Redirect back and show error message
dd('Login failed!');
}
}
----- EDIT -----
I've run the artisan command and made this middleware as you suggested :
namespace App\Http\Middleware;
use Closure;
use Request;
class GroupPermissions
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $group)
{
// Check User Group Permissions
if( $request->user()->group === $group ){
// Continue the request
return $next($request);
}
// Redirect
return redirect('restricted');
}
}
and edited this line into Kernel.php into $routeMiddleware :
'group.perm' => \App\Http\Middleware\GroupPermissions::class
I think this is done right so far, correct me if I'm wrong! Could I then do something like this to restrict the controllers?
Route::group(array('before' => 'auth'), function() {
Route::group( ['middleware' => 'group.perm', 'group' => 'normal'], function(){
Route::get('/normal/index', 'DummyNormalController#index');
});
Route::group( ['middleware' => 'group.perm', 'group' => 'extended'], function(){
Route::get('/extended/index', 'DummyExtendedController#index');
});
});
Ok, here is what you might do. Once user is logged in, you would check his credentials, get his user_group and decide what controller he should be redirected to.
if( Auth::attempt( $logindata ) ){
$user = Auth::user();
if ($user->inGroup('normal')) {
return redirect()->route('normal_controllers_named_route');
}
return redirect()->route('extended_controllers_named_route');
}
return redirect()->back()->withFlashMessage('don\'t get me wrong');
This will handle right routing after logging in.
The next portion where you need to protect you routes from unwanted user groups may be achieved with middlewares.
do an artisan command php artisan make:middleware ShouldBeInGroup
go to app/http/Kernel.php and add your new middleware to the routeMiddleware array. Key of the item might be anything you like. Let's call in inGroup. So: 'inGroup' => 'App\Http\Middleware\ShouldBeInGroup'
Now, in your controller, in constructor, you are able to call this middleware
$this->middleware('inGroup:extended'); //we also passing the name of the group
at lastly, work on the our middleware. Open newly created ShouldBeInGroup class and edit the handle method.
public function handle($request, Closure $next, $groupName)
{
if (Auth::check() && Auth::user()->inGroup($groupName)) {
return $next($request);
}
return redirect('/');
}
And finally you should work on inGroup method, that should return true of false. I assume that you have user_group field your users table. Then in your User eloquent model add the method
public function inGroup($groupName) {
return $this->user_group == $groupName;
}
Edit
if you want to use this middleware in your routes, you can do the following
Route::group(array('before' => 'auth'), function() {
Route::get('/normal/index', ['middleware' => 'group.perm:normal', 'uses' =>'DummyNormalController#index']);
}
But generally it's better to put all your middlewares into your Controller's constructor
public function __construct(){
$this->middleware('group.perm:normal'); // you can also pass in second argument to limit the methods this middleware is applied to : ['only' => ['store', 'update']];
}
And also on this note, Laravel provides built in auth middleware that you can use
public function __construct(){
$this->middleware('auth');
$this->middleware('group.perm:normal');
}
so then your routes would become much cleaner, just:
Route::get('normal/index', 'DummyNormalController#index');
I think the best way to do that is using middlewares. See the doc here
You can easily create a middleware using the following artisan command:
php artisan make:middleware ExtendedMiddleware
If you can't or don't want to use artisan, you need to create a class in The App/Http/Middleware folder.
In this class you'll need the following method to handle the request. In the method you can check for the user group.
public function handle($request, Closure $next)
{
// check user group
if( user_group_ok )
return $next($request); // Continue the request
return redirect('restricted'); // Redidrect
}
You can then use this middleware in your route.php file:
Route::group(['middleware' => 'auth'], function()
{
// Logged-in user with the extended group
Route::group(['middleware' => 'extended'], function()
{
// Restricted routes here
});
// Normal routes here
});
You can create a Middleware called : PermissionFilter
In PermissionFilter, you check if requesting user is in the group or not.
I can't provide a demo for now, but if you want I can make a demo later.
L5 middleware: http://laravel.com/docs/5.1/middleware
I defined a resource route group
Route::group(['prefix' => 'api/v1'], function() {
Route::resource('words', 'WordController');
});
and I created a controller for all that routes. I want to set basic authentication for all requests so I added to the constructor of WordController: $this->beforeFilter('auth.basic'); But there is no effect. I still can get all words without any username and password provided. Does someone know why?
class WordController extends ApiController {
protected $wordTransformer;
function __construct(WordTransformer $wordTransformer)
{
$this->wordTransformer = $wordTransformer;
$this->beforeFilter('auth.basic');
//$this->middleware('auth.basic');
}
public function index()
{
$words = Word::all();
return $this->respond([
'words' => $this->wordTransformer->transformCollection($words->all())
]);
}
}
If you are using laravel 5, you can use middleware that replace filter. Using middleware is becoming the preferred practice and way of thinking about decorating your routes. Why your code not working because auth.basic is a type of middleware not filter.
You can attach the middleware in controller since you are using Route::group.
See the code below how to attach it.
Route::group(['prefix' => 'api/v1', 'middleware' => 'auth.basic'], function() {
Route::resource('words', 'WordController');
});
You can see at the above code use middleware name "auth.basic". How do you know the middleware. Before you can use the middleware, you must register the middleware by define the middleware in /app/Http/Kernel.php. If you open that file you can see the code below.
/**
* The application's route middleware.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
];
You can try something like below. authenticate user during the routing rather then controller.
Route::get('home', array('before' => 'auth', 'do' => function()
{
// your action here
}));
Route::filter('auth',function(){
if(Auth::guest())
return Redirect::to('login');
});