laravel 5.5 Get user details inside constructor - php

I am building an application with multiple user roles and actions. I did follow the official laravel doc (https://laravel.com/docs/5.5/middleware#middleware-parameters).
But in my controller's constructor (from where I call the above middleware) I am using Auth facade to get user details. I know how to use Auth facade, I had implemented it on several places inside my application. But when I use it inside the constructor it returns null (in logged in condition - I double checked that).
I implemented it like this, I have to call two controllers(since only registered users can access that page)
public function __construct()
{
$role = Auth::user()->role;
$this->middleware('auth');
$this->middleware('checkRole:$role');
}
PS: I tried to initialize $role variable as protected and outside the constructor , still not working. Any suggestions will be helpful
Thank you.

That's because constructors are created before middlewares,that's why its returning null.
This answer will propably solve your problems: Can't call Auth::user() on controller's constructor

If you are using the same user table for both "front-end" user and "admin" & want to apply condition in admin controller's constructor.
You can use below.
auth()->user()
And in the constructor you can use below code.
public function __construct(){
$this->middleware(function ($request, $next) {
if(auth()->user()->hasRole('frontuser')){
return redirect()->route('home')->withFlashMessage('You are not authorized to access that page.')->withFlashType('warning');
}
return $next($request);
});
}
But I prefer to handle these in separate middleware class instead of writing this in the controllers constructor.

Related

Laravel - Authorization Works from Controller Method only

I have setup my model policy and it seems to be working when I authorize actions from within controller actions.
// create action
public function create()
{
$this->authorize('create', BusinessProfile::class);
return view('business-profile.create');
}
The policy for create simply returns true or false and switching the Boolean seems to be working as I am authorized based on it.
This conforms that my policies are set up correctly.
However, instead of using the authorize method everywhere in my controller, I tried to set up the middleware in my constructor.
The Laravel documentation shows this example.
Route::post('/post', function () {
// The current user may create posts...
})->middleware('can:create,App\Post');
So, I wrote this in my controller constructor.
public function __construct()
{
$this->middleware('auth');
$this->middleware('can:create,BusinessProfile')->only('create');
}
However, when doing this, the action is always unauthorized.
Bonus Information
I went ahead and wrote garbage code in my policy to raise a syntax error and still, I get an unauthorized response which tells me my policy is not firing at all. It could be that I have not registered my policy correctly but as mentioned above, $this->authorize(...) works as expected.
It seems there you used alias for your model while it requires model name. At documentation states:
some actions like create may not require a model instance. In these
situations, you may pass a class name to the middleware. The class
name will be used to determine which policy to use when authorizing
the action:
You can find more information here: https://laravel.com/docs/5.4/authorization#policy-methods
So in the controller constructor this line:
$this->middleware('can:create,BusinessProfile')->only('creat‌​e');
will become:
$this->middleware('can:create,App\BusinessProfile')->only('c‌​reate');

Auth::check() returns false Laravel 5.4

I have created the routes and views needed for authentication using one simple command:
php artisan make:auth
Everything works fine login and register sections. However when I go to my controller's constructor to check if the user its logged in I always get false response; even though the user its logged in!
public function __construct()
{
dd(Auth::check());
}
Any idea?! And yes I did use Auth; at the top.
Middleware (and therefore setting the logged in user) don't happen until after the controller constructor. See this related question/answer for more details:
Laravel 5 Auth is non object in Controller construct
Use the helper function auth()->check() and add
$this->middleware('auth') to the function __construct() method.

Laravel route group parameters

I want an app to use a URL structure something like this:
/account/johnsmith/photos
/account/johnsmith/messages
/account/johnsmith/settings
/account/joebloggs/photos
So users may add multiple accounts and then the route group selects the account automatically.
Route::group(['middleware' => 'auth', 'prefix' => 'account/{username}'], function () {
Route::get('/photos', 'PhotosController#index')->name('photos.index');
});
In the above example I can access the {username} parameter inside of PhotosController#index.
Is there a way to write some middleware that gets the account information automatically and it's accessible to all child routes in the group? Or am is this a bad way to try to build this?
This should be possible with route model binding.
Route::bind('username', function ($username) {
return Account::findByUsername($username);
});
Note: The above code could be put in your route provider or within the route group its self.
When done, you will pass the model Account into your controller methods as the first argument and it will automatically be the one matching that username. Or a 404 if it does not exist.
// If {username} is 'bob', the Account object now references Bob's account.
public function index(Account $account) {}
See Laravel Docs Here
Yes, if you need to perform a operation before all your controller methods get the request o propagate some common data you should use Laravel middlewares. It will not only centralise your logic as well as make your controller code neat.
Even laravel sometimes want you to do that.
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
So you can either write a middleware to calculate account details and if you are using the laravel version 5.3.4 and above you can directly assign it to the controller properties in constructor using closure based middlewares.
Like this:
class ProjectController extends Controller
{
/**
* All of the current user's account details
*/
protected $accountDetails;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->accountDetails= Auth::user()->accountDetails;
return $next($request);
});
}
}
Hope this would help.

Why can't I get the request attributes back in laravel controller contructor?

I am trying to get the authenticated user in the constuctor of my controller in laravel by doing dd(auth()->user()); and it says null. I even added the user id into a request attribute in one of my middleware like so:
$request->attributes->add(['auth_user_id' => $user_id]);
Even if I do dd($request->get('auth_user_id') in my controller's construct method, I get null. But when I do the same thing in a test route, Both die dump statements work well and give me back the user or the user id, whichever I ask for.
Why am I not able to get these in the construct method of my controller tho? I am even able to get the same user id and auth user in my controller method to which the request goes to. Just not the construct method. What am I missing?
With Laravel 5.3, this change was introduced where middleware are initialized after the controller class is constructed. This means app-critical middleware like Auth --specifically Auth::user() are not available to the controller's __construct() method.
Please refer this documentation.
https://github.com/laravel/docs/blob/5.3/upgrade.md#session-in-the-constructor
Edit
This way you can implement what you needed.
This will allow registering a closure middleware in the controller's constructor, so that any auth/session stuff can be called and set as properties on the controller:
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = $request->user();
return $next($request);
});
}
Refer this link by Controller closure middleware - JosephSilber
I think this is because constructor method called when the object of class initialized and at that time you are not logged in and when you are not logged in you cannot get the auth_user_id.
But In case of normal method, they are called after constructor method, and you are logged in that's why you are able to get the auth_user_id

Slim PHP Route in Middleware

In Slim is it possible to get the current route within middleware?
class Auth extends \Slim\Middleware{
public function call(){
$currentRoute = $this->app->getRoute(); // Something like this?
}
}
I know you can call $app->router()->getCurrentRoute() after the slim.before.dispatch hook is called, but when you call this from middleware it returns a non-object. Any help would be greatly appreciated.
Yes and no. If you look at the source code for Slim, you will see that registered Middlewares are called in LIFO order when the Slim::run method is called, and then Slim runs it's own "call" method where the processing of the request begins. It is in this method that Slim parses and processes the route. In which case, you cannot access $app->router()->getCurrentRoute() in the Middleware::call method because it won't have been parsed and defined yet.
The only way to do this is to register a listener on slim.before.dispatch inside your Middleware, and implement whatever you want to do in that method.
From the name of your class I assume you are trying to create a basic authentication module? I've done something similar to this before, and it went something like this:
class AuthMiddleware extends \Slim\Middleware
{
public function call()
{
$this->app->hook('slim.before.dispatch', array($this, 'onBeforeDispatch'));
$this->next->call();
}
public function onBeforeDispatch()
{
$route = $this->app->router()->getCurrentRoute();
//Here I check if the route is "protected" some how, and if it is, check the
//user has permission, if not, throw either 404 or redirect.
if (is_route_protected() && !user_has_permission())
{
$this->app->redirect('/login?return=' . urlencode(filter_input(INPUT_SERVER, 'REQUEST_URI')));
}
}
}
In this example, the onBeforeDispatch method will be run before of the route handlers are invoked. If you look at the source code, you can see the events are fired inside a try/catch block that is listening for the exceptions thrown by $app->redirect() and $app->pass(), etc. This means we can implement our check/redirect logic here just as if this was a route handler function.
Above is_route_protected and user_has_permission are just pseudo-code to illustrate how my auth middleware worked. I structured the class so that you could specify a list of routes or regex for routes in the Middleware constructor that were protected, as well as passing a service object that implemented the user permission checking, etc. Hope this helps.
There is an alternative method of doing this, as I've been in the same situation. What I wanted to avoid was matching anything by route and wanted to use route names instead, so you could try the following:
public function call() {
$routeIWantToCheckAgainst = $this->slimApp->router()->urlFor('my.route.name');
$requestRoute = $this->slimApp->request()->getPathInfo();
if ($routeIWantToCheckAgainst !== $requestRoute) {
// Do stuff you need to in here
}
$this->next->call();
}
You could even have an array of routes you DON'T want the middleware to run on and then just check if it's in_array() etc and if not, do what you need to.
You should use app->request()->getPathInfo() instead of app->getRoute().
class Auth extends \Slim\Middleware{
public function call(){
$currentRoute = $this->app->request()->getPathInfo();
}
}

Categories