I am developing a simple web application with Laravel 5.1 and my development environment is Homestead.
I have a view composer to pass Auth::user() data to admin panel related views automatically. Most general admin panel pages (Dashboard, Settings etc.) uses AdminController, and it extends Laravel's Controller. Specific admin panel pages (Users, Orders etc.) has their own controllers (Admin\UsersController, Admin\OrdersController respectively), which are extends AdminController.
No any middleware registered in routes for admin panel related routes, instead AdminController loads the auth middleware (which checks if registered user tries to load the page). And no other controller that extends AdminController overrides the constructor.
My problem is that if user is not logged in and tries to load an admin panel page (doesn't matter which one; Dashboard, Settings, Users, Orders - because the view composer called for every single one to pass Auth::user() data) there is no warning says "You are not authorized." or no redirection to login page, just an exception thrown which says Auth::user() is null.
Doesn't the auth middleware called first? If not what should I do to prevent the exception to be thrown (returning from view composer is not an elegant solution for my point of view by the way)?
Thanks in advance.
Addendum
AdminController
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class AdminController extends Controller
{
public function __construct()
{
// After middlewares
$this->middleware("auth");
$this->middleware("admin");
// Before middlewares
$this->middleware("no-cache");
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
return view("admin.index");
}
}
Admin\OrdersController
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Order;
class OrdersController extends Controller
{
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
$orders = Order::with("address")->get();
return view("admin.orders.index")->with("orders", $orders);
}
}
ComposerServiceProvider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Auth;
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
view()->composer("layouts.admin.default", function ($view) {
$admin = Auth::user();
$view->with([
"admin" => $admin,
"picture" => $admin->pictures[0]
]);
});
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
Note: ComposerServiceProvider is registered in config/app.php.
Your Admin\OrdersController extends App\Http\Controllers\Controller, when it should extend App\Http\Controllers\Admin\AdminController. That's why you are not getting a redirection.
Related
I have a boilerplate Laravel app, with a model generated with the cli command php artisan make:model Post -a --api to make an API controller, with form request and policies.
The Laravel Policy Authorisation docs doesn't seem to make it clear what to do with both a Policy and FormRequest. Do I call the policy class inside the FormRequest? Or ignore the policies for store/update?
How do I use auth policies with FormRequests for my API controller?
Version
Laravel 9
Although its not told directly in the docs. You can use the policy inside the authorize() method in a Form Request :
Authorization Using Model
class UpdatePostRequest extends FormRequest
{
public function authorize() : bool
{
return $this->user()->can(
'update', $this->post
);
}
}
Controller
class PostController
{
public function update(UpdatePostRequest $request, Post $post)
{
// your code here
}
}
So Instead of using $this->authorize('update', $post) inside the controller you can directly put it inside the FormRequest.
Hope it helps : )
Docs didn't make it clear, posting incase anyone else is struggling. Example for User model, UserPolicy and UserController.
First, add the Policy class in AuthServiceProvider.
App\Providers\AuthServiceProvider
/**
* The policy mappings for the application.
*
* #var array<class-string, class-string>
*/
protected $policies = [
User::class => UserPolicy::class,
];
Second, use authorizeResources in the controller to auto map policies to the api controller. See here for what the policy -> controller maps to
// App\Http\Controllers\UserController
use App\Models\User;
use Illuminate\Http\Request;
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
class UserController extends Controller
{
/**
* Create the controller instance.
*
* #return void
*/
public function __construct()
{
// Sets up user policy for this controller
$this->authorizeResource(User::class, 'user');
}
...
}
Last, DELETE the authorize section from the FormRequests
// App\Http\Requests\UpdateUserRequest
class UpdateUserRequest extends FormRequest
{
// DELETE the auth part below, otherwise it'd mess up using policies.
// I'm pretty sure this takes precedence over policies
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
//public function authorize()
//{
//return true;
//}
}
Now the policies set in UserPolicy will be used as auth guards for the User Controller.
I am trying to use laravel policies to check if a story is "visible" and if it isn't, if the authenticated user ownes the story (in which case he can still view it). I set up my policy using
php artisan make:policy StoryPolicy --model=Story
There I set up the checks required to see if the authenticated user can see the Story or not
<?php
namespace App\Policies;
use App\User;
use App\Story;
use Illuminate\Auth\Access\HandlesAuthorization;
class StoryPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view the story.
*
* #param \App\User $user
* #param \App\Story $story
* #return mixed
*/
public function view(User $user, Story $story)
{
if ($story->visibility == 'visible') {
return true;
} else {
return $story->user_id == $user->id;
}
}
}
I register the policy in my AuthServiceProvider
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Story' => 'App\Policies\StoryPolicy'
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
}
}
As I understand it, I should be able to use this policy in my controller. That is what I did.
<?php
namespace App\Http\Controllers;
use App\Storyblock;
use App\User;
use Illuminate\Http\Request;
use App\Category;
use App\Story;
class StoryController extends Controller
{
public function __construct(){
$this->middleware('auth')->except('show');
}
// GET shows the story page
public function show(Story $story) {
$this->authorize('view',$story);
return view('story.show', compact('story'));
}
}
this however always result in a 403
I've tried many things, changing up the way the policies are set, changing the logic dd'ing if all is correct. After 4 hours of lookign online I failed to come up with an answer.
Also, in my phpStorm I noticed that my policy files are indicated red with no usages over the entire project. This makes me think that I somehow fail to import them in my AuthServiceProvider
i don't see all your code but you get this message when you want to access of lavarel resouce and you don't have the scrf token make sure all your view or on the app view header you have this
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
i think you just try to get a resource without this then it just tell you don't have access of this one.
You have specified that the show method isn't going to have the auth middleware assigned, so there may not be an authenticated User. From the Policy documentation:
"By default, all gates and policies automatically return false if the incoming HTTP request was not initiated by an authenticated user"
You will need to continue reading the Policy documentation for how to handle the lack of an authenticated user, guest.
Laravel 5.7 Docs - Authorization - Writing Policies - Guest Users
I'm trying to executing a function in every page and I do that in AppServiceProvider.php in boot() I dependent on Auth class but Auth::check() always return false
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Auth;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
dd(Auth::check());
}
}
From the Laravel Docs
Service providers are truly the key to bootstrapping a Laravel application. The application instance is created, the service providers are registered, and the request is handed to the bootstrapped application. It's really that simple!
Once the application has been bootstrapped and all service providers have been registered, the Request will be handed off to the router for dispatching. The router will dispatch the request to a route or controller, as well as run any route specific middleware.
and since Auth and Session are updated / initialized using a middleware, it means that you can't access to it from a Service Provider.
you can only bind data to views in your service providers using callbacks that are called when the view is rendered ( it means that the server is already preparing the response )
View::composer('is_authenticated', Auth::check());
Maybe Auth is not load on before AppServiceProvider. Because in Controllers Auth::check() work well. So i think using Auth::check() in AppServiceProvider is very bad idea. AppServiceProvider intended to register and bootstrap services, maybe there a better place for Auth::check() in you app?
you need view composer for this.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Auth;
use DB;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
view()->composer('*', function ($view)
{
if (Auth::check()) {
}
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
To correctly protect a route for only authorised users, you should use ->middleware('auth') on your route.
https://laravel.com/docs/5.8/authentication
Also I would suggest updating your question to include the Laravel version you are using.
I checked controller.php in Laravel 5 and it only has a base controller. In Laravel 4 you also have a home controller. Is the home controller removed in Laravel 5?
L5 comes with no HomeController like previous versions. But, you can create a new controller using the command
php artisan make:controller HomeController
or you can manually create one. But make sure you are extending the Controller.php class.
eg:
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Show the application dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('home');
}
}
So I'm building a Symfony web app.
I have a simple controller (DefaultController.php) as follows:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends Controller
{
/**
* #Route("/", name="default")
*/
public function defaultAction(){
return $this->render('default/hello.html.twig', array(
'name' => "hello"
));
}
}
Nothing special.
Now, I would like to have a separate .php file called APIController.php that gets called when a user navigates to http://eamorr.com/api/
APIController.php would then handle requests such as:
http://eamorr.com/api/getUser
http://eamorr.com/api/addUser
http://eamorr.com/api/getAllUsers
...
Here's what APIController.php should look like:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class APIController extends Controller
{
/**
* #Route("/getUser", name="getUser")
*/
public function getUser(){
//
}
/**
* #Route("/addUser", name="addUser")
*/
public function addUser(){
//
}
/**
* #Route("/getAllUsers", name="getAllUsers")
*/
public function getAllUsers(){
//
}
//etc.
}
From an architectural point of view, am I doing this right? Does anyone have any recommendations as to how to do this in Symfony?
If you define routes like:
/**
* #Route("/getUser", name="getUser")
*/
public function getUser(){
//
}
Then the URL to this action will be http://eamorr.com/getUser. As you can see there's no /api part, and this is because you didn't mention it anywhere.
You have two solutions for this case.
First it to define full routes like
/**
* #Route("/api/getUser", name="getUser")
*/
public function getUser(){
//
}
Second: since you want all APIController actions to have this /api part, you can define a prefix for all routes by defining "base" route for whole class.
/**
* #Route("/api", name="getUser")
*/
class APIController extends Controller
Then you can leave your actions' routes like they are.
More info: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html#route-prefix
You are on the right track. But if you are looking to support multiple request and response formats (eg. JSON, XML) you are better off using FOSRestBundle.
It can handle content negotiation, entity serialization (using JMSSerializerBundle which is used by the FOSRest Bundle) and it let's you build RESTful routes.