Adding custom function to Policy - laravel

I want to create a custom function for my Policy but I don't know how to use it in the route as middleware or something. Basically below is the code for my policy
class DepartmentPolicy
{
use HandlesAuthorization;
public function list(User $user, Department $department)
{
return true;
}
}
Now I want to call this in my Route something like this
Route::prefix('/departments')->group(function () {
Route::get('/list', [App\Http\Controllers\DepartmentController::class, 'list'])
->middleware('can:list,department');
});
But I always get a 403 function. When I remove this middleware I can access the url. But with this I get a 403.
Note: I already created the policies in the AuthServiceProvider like below
protected $policies = [
'\App\Models\User' => 'App\Policies\UserPolicy',
'\App\Models\Department' => 'App\Policies\DepartmentPolicy',
];
Can anyone tell me how to use this custom function in my custom route? Basically I know that by default if I access the index in the controller it will call the viewAny function in my DepartmentPolicy but what about the custom function in the policy and the custom route? how can I call that or link each other?

Related

Laravel Authorization can always return false for API

I am trying to use Laravel authorization policies with API and Sanctum. However, I use middleware on the route as follows.
Route::get('/user/orders/{order}',
[OrderController::class, 'get_user_order_detail'])
->middleware('can:view:order');
OrderPolicy.php
namespace App\Policies;
use App\Models\Order;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class OrderPolicy
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* #return void
*/
public function __construct()
{
// dd(1);
}
public function view(User $user, Order $order): bool
{
return $user->id === $order->user_id;
}
}
As you see, when I add dd(1) inside the constructor of the OrderPolicy, then I get 1 as expected, but when I move it to the inside of view function, I get unauthorized which indicates that is maybe the view function itself is not being called, but, the OrderPolicy is getting called.
Your middleware definition is wrong:
->middleware('can:view:order')
it should be:
->middleware('can:view,order')
From the docs:
Laravel includes a middleware that can authorize actions before the incoming request even reaches your routes or controllers. By default, the Illuminate\Auth\Middleware\Authorize middleware is assigned the can key in your App\Http\Kernel class. Let's explore an example of using the can middleware to authorize that a user can update a post:
use App\Models\Post;
Route::put('/post/{post}', function (Post $post) {
// The current user may update the post...
})->middleware('can:update,post');
In this example, we're passing the can middleware two arguments. The
first is the name of the action we wish to authorize and the second is
the route parameter we wish to pass to the policy method. In this
case, since we are using implicit model binding, a App\Models\Post
model will be passed to the policy method. If the user is not
authorized to perform the given action, an HTTP response with a 403
status code will be returned by the middleware.

Laravel policy not working

In my Laravel application, i have several policies working, but one will not work.
Controller
public function store(Project $project, CreateActionRequest $request)
{
$this->authorize('store', $project);
Action::create([
'name' => $request->name,
]);
return redirect()->route('projects.show', $project->id)->withSuccess('Massnahme erfolgreich gespeichert');
}
Policy
namespace App\Policies\Project;
use App\Models\Project\Project;
use App\Models\User;
use App\Models\Project\Action;
use Illuminate\Auth\Access\HandlesAuthorization;
class ActionPolicy
{
use HandlesAuthorization;
public function store(User $user, Project $project)
{
return $user->company_id === $project->company_id;
}
}
AuthServiceProvider
protected $policies = [
'App\Models\User' => 'App\Policies\CompanyAdmin\UserPolicy',
'App\Models\Company' => 'App\Policies\CompanyAdmin\CompanyPolicy',
'App\Models\Team' => 'App\Policies\CompanyAdmin\TeamPolicy',
'App\Models\Department' => 'App\Policies\CompanyAdmin\DepartmentPolicy',
'App\Models\Location' => 'App\Policies\CompanyAdmin\LocationPolicy',
'App\Models\Division' => 'App\Policies\CompanyAdmin\DivisionPolicy',
'App\Models\Costcenter' => 'App\Policies\CompanyAdmin\CostcenterPolicy',
'App\Models\Workplace' => 'App\Policies\CompanyAdmin\WorkplacePolicy',
'App\Models\Product' => 'App\Policies\CompanyAdmin\ProductPolicy',
'App\Models\Project\Action' => 'App\Policies\Project\ActionPolicy',
'App\Models\Project\Project' => 'App\Policies\Project\ProjectPolicy',
];
CreateActionRequest
namespace App\Http\Requests\Project;
use Illuminate\Foundation\Http\FormRequest;
class CreateActionRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|min:3',
];
}
}
All policies are working except ActionPolicy and ProjectPolicy.
I added in the policy a __construct() method to check if the policy is called. But ActionPolicy and ProjectPolicy are not working.
How can i search the error? I tried with dd() but i got only allways the message: This action is unauthorized
Since you are injecting CreateActionRequest instead of Request that means you are defining your own set of rules to authorize the FormRequest which comes inside of your method. Further it means that you gotta define a few rules which the "FormRequest" has to pass in order to EVEN reach your controller, this is a nice concept that I like about Laravel since the code is not centralized, but rather spread and every layer has it's own responsibility. Now, you don't have to call any method from your CreateActionRequest nor you have to write any code regarding that class in your controller, because Laravel runs authorize method by default before allowing the Request to reach your controller, before running authorizemethod in your CreateActionRequest it runs rules method which verifies that all the given fields pass the expressions you assigned them, so the execution is something like this CreateActionRequest => rules => authorize => IF(authorized) Controller ELSE Not authorized, hope that makes sense. In order to fix your code:
1.) Remove $this->authorize('store', $project);
This will allow you to pass not authorized error in case your name passes the truth test inside of rules method inside of your CreateActionRequest. If you wish to utilize your Action Policy you will need to hook up your custom Request(CreateActionRequest) with it and this is how:
public function authorize()
{
$store = $this->route('project');
//The above line will return Project object if your mapping is correct
//If it's not it will return the value you passed to your route for {project}
return $this->user() && $this->user()->can('store', $store);
}
EDIT:
Here is the link where you can see how to properly authorize and connect policy with CreateActionRequest
Do you have all your controller methods defined with the Request object last?
public function store(Project $project, CreateActionRequest $request)
The Request object should be the first parameter in the methods signature:
public function store(CreateActionRequest $request, Project $project)
Dependency Injection & Route Parameters
If your controller method is also expecting input from a route parameter you should list your route parameters after your other dependencies.
Most Laravel authorization mechanisms have identical method signatures allowing them to work across varying classes.

Laravel registerPolicies always get "This action is unauthorized."

In laravel 5.5 I create the policy
public function view()
{
return true;
}
and register it in the AuthServiceProvider
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
Post::class => PostPolicy::class,
];
In the controller I use the policy like this:
$this->authorize('view');
I get the error This action is unauthorized whether the function view() returns true or false.
Your policy is registered for the Post model.
I assume your view() method is inside the PostPolicy class. It appears as if you'd want to use it without a model instance.
Use $this->authorize('view', Post:class); if the policy code does not require a model instance.
Your view method should furthermore receive a user model.
public function view(User $user) { ... };
Otherwise, for whom would you want to check permissions.

Laravel - middleware on routes in controller

I have a route resource
Route::resource('campaign', 'CampaignController');
I want to restrict some of these routes to the users.
For example the index page lists all the campaigns and they should not see this only their own ones.
I have a custom middleware that simply checks the if the user is an admin
However I cannot apply this to individual methods.
public function index()
{
$this->middleware('checkuser');
}
Just the constructor
public function __construct()
{
$this->middleware('checkuser');
}
How do I get around this and apply to individual route in the controller
Sorry my mistake I should have read the docs you can add exceptions or allowed.
$this->middleware('auth');
$this->middleware('log', ['only' => ['fooAction', 'barAction']]);
$this->middleware('subscribed', ['except' => ['fooAction', 'barAction']]);
}

Laravel how to execute specific function every time a specific controller's functions are used

I am running laravel and i just implemented PHP Excel in order to export excels with data. For security reasons i want to redirect the user if his facebook id is no match with "my administrator facebook id".
My controller is named ExcelController and it has different functions inside it.
I do not want to use the below code inside every function so i am trying to find something to execute like __construct() - everytime this controller is accessed :
if(Auth::user()->facebook_id != env('facebook_id_admin_access')) {
return redirect()->route('home');
}
env('facebook_id_admin_access') is an .env variable(like a constant) with the facebook id i want to give access so it can use the specific controller.
I tried creating a public function __construct(){} in the controller and put the above code block but it doesn't get called.
Is this possible and how? Should i use middleware for that?
EDITED QUESTION
1) Created a middleware CheckAdminAccess
*/
public function handle($request, Closure $next)
{
if(Auth::user()->facebook_id != env('FACEBOOK_ID_ADMIN_ACCESS') || !Auth::check()) {
return redirect()->route('/');
}
return $next($request);
}
2) Updated in app\http\kernel.php to
protected $routeMiddleware = [
'admin_access' => 'App\Http\Middleware\CheckAdminAccess',
];
3) Updated in routes.php :
Route::get('/output/completed', 'ExcelController#completed')->middleware('admin_access');
But it doesn't seem to work, did i forgot anything?
After creating a middleware,
To make it work i changed in my routes.php from :
Route::get('/output/completed', 'ExcelController#completed')->middleware('admin_access');
To :
Route::group(['middleware' => ['auth','admin_access']], function() {
Route::get('/output/completed', 'ExcelController#completed');
});

Resources