I'm trying to check if the user has permission to a certain model. Up until now (with Laravel 5.2), I added this code at the constructor:
public function __construct()
{
if (!Auth::user()->hasPermission('usergroups')) {
abort(404);
}
}
Now, after upgrading to Laravel 5.3, Auth::user() returns null when being called from the controller's constructor. If I call it within any other method of the class, it returns the currently logged in user.
Any Ideas why?
See here:
Session In The Constructor
In previous versions of Laravel, you could access session variables or
the authenticated user in your controller's constructor. This was
never intended to be an explicit feature of the framework. In Laravel
5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
As an alternative, you may define a Closure based middleware directly
in your controller's constructor. Before using this feature, make sure
that your application is running Laravel 5.3.4 or above:
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class ProjectController extends Controller
{
/**
* All of the current user's projects.
*/
protected $projects;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->projects = Auth::user()->projects;
return $next($request);
});
}
}
Of course, you may also access the request session data or
authenticated user by type-hinting the Illuminate\Http\Request class
on your controller action:
/**
* Show all of the projects for the current user.
*
* #param \Illuminate\Http\Request $request
* #return Response
*/
public function index(Request $request)
{
$projects = $request->user()->projects;
$value = $request->session()->get('key');
//
}
class StudentController extends Controller
{
public $role;
public function __construct()
{
$this->middleware(function ($request, $next) {
if (!Auth::user()->hasPermission('usergroups')) {
abort(404);
}
return $next($request);
});
}
}
Hope help you!!!
Update:
Get your code inside
$this->middleware(function ($request, $next) {
//your code
return $next($request);
});
Because you're attempting to access the instance before the middleware even fires. You can use request()->user() instead.
Related
I am using a custom guard for a different type of user using a custom guard labelled business_user.
I have noticed I am able to login to as both normal users (web) and my business_users.
I've read in the Pusher documentation that I used to create my custom guards in the first place to add additional middleware into my "LoginController".
But I don't actually even have a LoginController, I've created my own controllers for each user type. AuthController (for web) and BusinessController (for business_user).
I have created a third controller labelled LoginController with the following code:
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = '/dashboard';
public function __construct()
{
$this->middleware('guest')->except('logout');
$this->middleware('guest:business_user')->except('logout');
}
}
I also updated my RedirectIfAuthenticated as follows:
class RedirectIfAuthenticated
{
public function handle($request, Closure $next, $guard = null)
{
if ($guard == "business_user" && Auth::guard($guard)->check()) {
return redirect('/dashboard');
}
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
return $next($request);
}
I also have a RedirectIfAuthenticated middleware inside my Middleware folder.
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
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 ($guard == "business_user" && Auth::guard($guard)->check()) {
return redirect('/dashboard');
}
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
return $next($request);
}
}
When I land on my user login page, it still allows me to attempt logging in. Can someone tell me how to resolve this?
In LoginController, you can override authenticated method.
/**
* The user has been authenticated.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function authenticated(Request $request, $user)
{
auth()->login($user); // this method will login with default guard
return redirect()->intended($this->redirectPath());
}
I think because the order of middleware
<?php
public function __construct()
{
$this->middleware('guest')->except('logout'); // this procress first and redirect to login page
$this->middleware('guest:business_user')->except('logout');
}
So, I think you can check directly in __construct() of LoginController or in login view (blade file)
#if (Auth::check('business_user'))
You are already logged in (or perform a redirect somewhere)
#else
//display login form
#endif
I created a custom guard (admin) in my application, and everything looks good following this tutorial online (https://pusher.com/tutorials/multiple-authentication-guards-laravel). The only problem is when i try to access a view that is protected by the admin guard, instead of giving me a exception NotAuthenticate, is giving me a "InvalidArgumentException
Route [login] not defined.".
My Dashboard controller custom guard is:
public function __construct()
{
$this->middleware('auth:admin');
}
But the strange thing that i cant understand whats going on is that when i add in my web.php routes the "Auth::routes();", it works fine, the Exception NotAuthenticate gets fired.
Is there a reason why my custom only works has expected with i add the Auth::routes() ?
Admin Login Controller:
namespace App\Http\Controllers\Admin\Auth;
use Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Validation\ValidationException;
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/admin/dashboard';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest:admin')->except('logout');
}
public function login(Request $request)
{
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
public function showLoginForm()
{
return view('admin.auth.login');
}
/**
* Log the user out of the application.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
return $this->loggedOut($request) ?: redirect('/admin');
}
/**
* Get the guard to be used during authentication.
*
* #return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard()
{
return Auth::guard('admin');
}
}
Try following the following tutorial, it did the trick for me.
On a side note: Avoid modifying the existing controller that laravel ships with, instead extend it and implement your own functionality. This practice will take you the long way.
https://www.codementor.io/okoroaforchukwuemeka/9-tips-to-set-up-multiple-authentication-in-laravel-ak3gtwjvt
I am creating an app in Laravel 5.4, where I have a middleware ValidateBooking and then a controller called with a URL like /booking/6/car, which lists all the cars assigned to that booking.
In the ValidateBooking middleware I am validating the booking id 6 in the above URL by using the Booking::find(6) Eloquent function. But what I want is, if the Booking exists then pass that object to the controller, so I do not fetch it again in the controller. I don't want to query the database twice for the same thing.
I tried a few methods to merge the model object with $request in the middleware, but was not able to access it in the controller properly.
My middleware code is:
<?php
namespace App\Http\Middleware\Booking;
use Closure;
use App\Booking\Booking;
class ValidateBooking
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$booking = Booking::find($request->booking_id);
if (!$booking) {
return redirect(route('booking.show', $request->booking_id));
}
$request->attributes->add(['bookingInstance' => $booking]);
return $next($request);
}
}
And then fetching it in the Controller like:
$request->get('bookingInstance')
It works if I pass any string value or something, but not for an object? Please advise what could be the best way for this.
You just be able to use the merge() function on the request object.
public function handle($request, Closure $next)
{
$booking = Booking::find($request->booking_id);
if (!$booking) {
return redirect(route('booking.show', $request->booking_id));
}
$request->merge(['bookingInstance' => $booking]);
return $next($request);
}
Apart from using $request->merge() which is proven to work, simply adding the model object to the request with the following also helps you access the model in the controller:
$request->booking = $booking;
The only thing to be aware of is just simply ensuring that there was never a parameter called booking so as not to override it.
What I think you want to look at is Explicit Route Model bindings.
If you add an explicit binding to the RouteServiceProvider, you can then access that parameter in your middleware and in your controller.
// app/Providers/RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::model('booking', \App\Booking\Booking::class);
}
// routes/web.php
Route::get('/booking/{booking}/car', 'BookingController#car')->middleware('booking.validate');
// app/Http/Middleware/ValidateBooking.php
public function handle($request, Closure $next)
{
$booking = $request->booking;
// ...
return $next($request);
}
// app/Http/Controllers/BookingController.php
public function car(Request $request)
{
dd($request->booking);
}
Here is what you can do. I had this problem once.
public function handle($request, Closure $next)
{
$booking = Booking::find($request->booking_id);
if (!$booking) {
return redirect(route('booking.show', $request->booking_id));
}
sesion()->put('bookingInstance', $booking);
return $next($request);
}
This is a simple.
I want to pass my input information ($request->all()) from my LoginController to my HomeController. How can I do that? The LoginController is generated by Laravel scaffold.
LoginController:
<?php
namespace App\Http\Controllers\Auth;
use \Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Auth\Authenticatable;
use Illuminate\Http\Request;
use App\Account;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct(Request $request)
{
$this->middleware('guest', ['except' => 'logout']);
}
/**
* Override the username method used to validate login
*
* #return string
*/
public function username()
{
return 'username';
}
}
HomeController in which dd($request->all() returns []
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Blog;
use App\Account;
use Illuminate\Foundation\Auth;
use Illuminate\Support\Facades\Input;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
//$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$blogItems = Blog::all();
$onlinePlayers = Account::getOnlinePlayers()->count();
$onlineStaff = Account::getOnlineStaff()->count();
//return view('home.index', compact('blogItems', 'onlinePlayers', 'onlineStaff'));
return dd($request->all()); //This returns an empty array
}
}
Quick and dirty
The following method is a quick and dirty method. It relies on how Laravel's authentication is designed under the hood. If Laravel changes this in the next version, this may need to change with it.
In your LoginController, implement your own login() method. You will need to rename the login() method provided by the trait because we still want to call it:
class LoginController extends Controller
{
use AuthenticatesUsers {
login as traitLogin
}
public function login(Request $request)
{
$request->session()->flash('form_type', 'login');
return $this->traitLogin($request);
}
}
In your RegisterController, implement your own register() method. You will need to rename the register() method provided by the trait because we still want to call it:
class RegisterController extends Controller
{
use RegistersUsers {
register as traitRegister
}
public function register(Request $request)
{
$request->session()->flash('form_type', 'register');
return $this->traitRegister($request);
}
}
Now, in your HomeController, you can get the type of form that was submitted from the flashed session data.
class HomeController extends Controller
{
public function index(Request $request)
{
$form = $request->session()->get('form_type');
// the rest of your logic
}
}
Cleaner
In contrast to the method above, I would suggest using a hidden form value and a new middleware to process that form value.
This is a little bit cleaner because it doesn't rely on any of the built in Laravel authentication logic. If Laravel changes the name of the traits, the method names, the route actions, or the actual logic used inside the methods, it won't affect how this functionality is designed or how it works.
In your login and register forms, add a new hidden field:
Login form:
<input type="hidden" name="form_type" value="login" />
Register form:
<input type="hidden" name="form_type" value="register" />
Now, create a middleware that will process this new form element and flash the value to the session.
class FlashFormType
{
public function handle($request, Closure $next)
{
$response = $next($request);
if ($request->has('form_type')) {
$request->session()->flash('form_type', $request->input('form_type'));
}
return $response;
}
}
From here, you can either add this middleware to the web middleware group in app/Http/Kernel.php so it is used for all web requests, or you could just add this middleware to the LoginController and RegisterController constructors so that only they use it.
Once you've assigned the middleware somewhere, update your HomeController to access your flashed data:
class HomeController extends Controller
{
public function index(Request $request)
{
$form = $request->session()->get('form_type');
// the rest of your logic
}
}
NB: none of the provided code is tested. treat as pseudo-code.
If you want to get current user, that was authenticated with your login data, then \Auth:user() will go the trick. Here is index() of HomeController:
public function index()
{
return \Auth::user();
}
If you really need input data (actually I cannot imagine the case) , a solution may be to put request->all() in session session(['login_data' => $request->all() ]) and then retrieve it via session('login_data'). If you go that way, add this method to your LoginController:
/**
* Send the response after the user was authenticated.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
protected function sendLoginResponse(Request $request)
{
$request->session()->regenerate();
$this->clearLoginAttempts($request);
session(['login_data' => $request->all() ]);
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
This overrides original sendLoginResponse() method of AuthenticatesUsers trait. Then in HomeController put this:
public function index()
{
return session('login_data');
}
Hope this helps!
tried to implement a simple user register/login function on my site using the laravel default controllers (auth/password), but as soon as I login, the class RedirectIfAuthenticated handle function prevents all access to auth url's, thus I cannot logout anymore. Is there a bug and I need to write an exception on the handle function or have I missed something?
Here is how the class looks like by default:
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)
{
//dd($next($request));
if (Auth::guard($guard)->check()) {
return redirect('/articles');
}
return $next($request);
}
}
The AuthController's constructor should look similar to this:
public function __construct()
{
$this->middleware('guest', ['except' => 'logout']);
}
The guest middleware is handled by the RedirectIfAuthenticated class and, in order to have the logout functionality working, you should choose one:
call the logout method from your AuthController.
call whichever method you use for logout and exclude it in AuthController's constructor:
public function __construct()
{
$this->middleware('guest', ['except' => '<whichever_method>']);
}
For potentially more-advanced reasons and needs, I will show a different idea.
Inside any middleware, a person could implement their own except list. Here is a reference:
<?php
namespace App\Http\Middleware;
use Closure;
class CustomThing
protected $except = [
'api/logout',
'api/refresh',
];
public function handle($request, Closure $next)
{
foreach ($this->except as $excluded_route) {
if ($request->path() === $excluded_route) {
\Log::debug("Skipping $excluded_route in this middleware...");
return $next($request);
}
}
\Log::debug('Doing middleware stuff... '. $request->url());
}
}
I will leave it up to imagination to extend that to support other types of URLs. For example, investigate matchers such as $request->url(), $request->fullUrl(), and $request->is('admin/*').
A person could investigate the vendor code for the VerifyCsrfToken middleware and make their custom one support something like this:
protected $except = [
'api/logout',
'api/refresh',
'foo/*',
'http://www.external.com/links',
];
If you want it to be a reuseable solution, make that matching algorithm a Trait and import it into any middleware you want to exclude routes from.