Laravel authorizeResource "This action is unauthorized." - php

I have a model called BusinessClient in the namespace App/BaseData. For this model I've created a Policy with the command php artisan make:policy BaseData/BusinessClientPolicy --model=BusinessClient and registerd it in th e AuthServiceProvider.php file.
protected $policies = [
// Base Data
BusinessClient::class => BusinessClientPolicy::class,
];
The viewmethod from this policy looks like this
public function view(User $user, BusinessClient $businessClient)
{
return true;
}
To authorize my resource I called in the constructor of my apiResource Controller the authorizeResource method.
public function __construct(Request $request)
{
$this->middleware('auth:api');
$this->middleware('verified');
$this->authorizeResource(BusinessClient::class, 'business_client');
}
My problem is that I alwas get a 401 Forbiddenresponse when I try to call the show method from my controller. What is going wrong with my code?

Related

Laravel can't create policy for user model

Can' create Policy for User model.
I created Policy like this
php artisan make:policy UserPolicy --model=User
Got UserPolicy.php with CRUD actions.
Then inside AuthServiceProvider.php I added
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
User::class => UserPolicy::class,
];
But nothing happens. As I understand generated Policy for User model by default returning false on every action, I even explicitly added this to UserPolicy class:
public function create(User $user)
{
return false;
}
Still can create user.
Later I will need to check if the user trying to edit his own post or not. Everything should be forbidden for non-admin users except editing own profile (model).
I must be missing something obvious.
UPDATE:
If I put
$this->authorize('create', $user);
In UsersController create method, it will invoke create method Policy, so it seams that something is wrong with
...
use App\Policies\UserPolicy;
use App\User;
...
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
User::class => UserPolicy::class,
];
inside AuthServiceProvider.
You can write this code for User Policy
in UserPolicy.php :
public function update(User $user, User $model)
{
return $user->id === $model->id;
}
For example by this code you can update just your own profile.
You need to put this in you function in controller
$this->authorize('create',$user)
Put below lines in your controller function $this->authorizeForUser($currentUser,'create', User::class)

Laravel: understanding the "credentials" method mechanism

I need the custom authentication, namely:
Custom model and DB table for LoginController (solved)
"Order ID" instead of email for authentication (solved)
Because I do not understand the credentials method mechanism, I get the error:
Type error: Argument 1 passed to Illuminate\Auth\EloquentUserProvider::validateCredentials()
must be an instance of Illuminate\Contracts\Auth\Authenticatable,
instance of App\Order given, called in C:\OpenServer\domains\sites-in-development.loc\vendor\laravel\framework\src\Illuminate\Auth\SessionGuard.php on line
<?php
class LoginController extends Controller {
use AuthenticatesUsers;
protected $redirectTo;
protected $orderAlias;
protected $loginView;
/*
Here I set the order ID as "username". I don't like this method,
because it causes the confusion ($username is actually order ID),
but don't know the other way yet.
REF is just the class with array contains the string values to avoid hardcoding
*/
protected $username = REF::tables['orders']['fieldNames']['orderId'];
public function __construct() {
$this->middleware('guest')->except('logout');
$this->loginView = '00_top';
}
public function showLoginForm() {
// Work properly
}
public function login(Request $request) {
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
// manual validation (work properly)
// getting $orderAlias from DB
$redirectTo = '/'.$orderAlias;
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request); // to "credentials"...
}
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
protected function credentials(Request $request) {
// here is error
return $request->only(REF::forms['loginToOrder']['fieldNames']['orderID'],
REF::forms['loginToOrder']['fieldNames']['password']);
}
}
The model you want to use has to implement the Authenticatable interface/contract.
Laravel - Docs - Authentication - The Authenticatable Contract

Not working laravel Policy for my UsersUpdate

I have
(1/1) HttpException
This action is unauthorized.
I think all should work fine and I have done it right but maybe not.
My controller method:
public function update(Request $request, Users $uzytkownik)
{
$this->authorize('update', $uzytkownik);
return 1;
}
UsersPolicy that is in App\Policies\:
<?php
namespace App\Policies;
use App\Models\Users;
use Illuminate\Auth\Access\HandlesAuthorization;
class UsersPolicy
{
use HandlesAuthorization;
public function update(Users $user)
{
return true;
// return $user->login === auth()->login;
}
}
And in AuthServiceProvider:
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
'App\Models\Users' => 'App\Policies\UsersPolicy',
];
My Users model lays in App\Models\
When I cut $this->authorize('update', $uzytkownik); this line from controller everything works fine and I see '1', when I add it again HttpException.
What do I have wrong here? Thinking and Thinking, looking, I don't see anything bad here.
please make sure that your route is under auth middlware like this :
Route::group(['middleware' => 'auth'], function () {
// ur update route here
});
or in ur controller constructor like this :
public function __construct()
{
$this->middleware('auth');
}
and also like #Laerte said your update policy method should have another parameter of type user which is the user you want to edit, like this :
public function update(Users $userLoggedIn, Users $uzytkownik)
{
return true;
}
In your Policy, you have to add two parameters: The first one is the user logged in, and the second is the actual parameter. Try this in the Policy:
public function update(Users $userLoggedIn, $user)
{
return true;
}

Calling Custom request validation from resource controller

I need help to validate using Custom request class validation but since my controller is a resource controller I can't add the custom request class as parameter. Any ideas how to call custom request validation from resource controller?
This is the route to my Resource Controller
Route::resource('customers', 'CustomerController');
And this is my Customer Request class
class CustomerRequest extends Request
{
//...
public function rules()
{
return [
'customer_type'=>'required|',
'customer_vendor'=>'required|',
'customer_name'=>'required|',
'company_name'=>'required_if:customer_type,Company',
'job_position'=>'required|',
'street'=>'',
'city'=>'required|',
'country'=>'required|',
'website'=>'url',
'phone'=>'required_unless:mobile|',
'mobile'=>'required_unless:phone|',
'email'=>'email',
];
}
}
And this is my Resource controller:
class CustomerController extends Controller
{
// ....
// Add Customer
public function store()
{
//how do i call custom request validation here
return view('create_views/new_customer',['title' => 'New Customer','nav_links'=>CustomerController::$Links]);
}
}
SOLVED
When you create the custom request using php artisan the authorise function is returning false and we need to return true if we are logged in:
public function authorize()
{
return false;//should be return true;
}
You just need to type-hint the request (CustomerRequest) on your controller method (store()).
public function store(CustomerRequest $request)
{
return view('create_views/new_customer',['title' => 'New Customer','nav_links'=>CustomerController::$Links]);
}
SOLVED
When you create the custom request using php artisan the authorise function is returning false and we need to return true if we are logged in:
public function authorize()
{
return false;//should be return true;
}

Laravel how to pass parameters to controller middleware

I'm trying to do something but i cant even imagine how to do it.
To pass parameters to a middlware I'm doing this:
Route::put('post/{id}', ['middleware' => 'role:editor', function ($id) {
//
}]);
From the laravel documentation.
Then get the parameter in the handle function...
But laravel suggest to declare the middleware use in the __construct in the Controller, insted in the route.
So...
public function __construct() {
$this->middleware('auth');
}
But i dont know how to pass parameters to the Controller.
I would thank any help.
You can access url parameters into middleware shown as below:
routes.php
Route::get('/test/{id}', ['uses' => 'Test#test']);
Test Controller
<?php
namespace App\Http\Controllers;
class Test extends Controller
{
/**
* Test constructor.
*/
public function __construct()
{
$this->middleware('test');
}
public function test()
{
return 'sample';
}
Test Middleware
<?php namespace App\Http\Middleware;
use Closure;
class Test
{
public function handle($request, Closure $next)
{
dd($request->id);
return $next($request);
}
}
** dont forget update your Kernel.php to activate middleware
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'test' => \App\Http\Middleware\Test::class,
];
Don't put the middleware in the controller. Keep business logic out of the controller as often as possible.
Instead, make your middleware file.
php artisan make:middleware MyMiddlewareDoesThings
Now inside of your file, locate the $handle function.
public function handle($request, Closure $next)
Just add arguments after the request and closure
public function handle($request, Closure $next, $role)
Now $role will contain the value of role:value which from your example would be $editor
#muhammad-sumon-molla-selim answer is actually the best one according to the question, but it leaks into explanation.
With my knowledge I was able to follow his guideline so here is a full example plus a usecase where Route's middleware or Request parameters is not possible.
I have an abstract CRUDControllerBase, which is extended by many child controllers.
Each child controllers need a different permission to perform any action on the model excepts index/show.
So I was forced to dynamically pass the permission (string) from the controller to the Middleware, here is the base structure:
// App\Http\Controllers\CRUDControllerBase.php
abstract class CRUDControllerBase extends Controller
{
/**
* #var string The needed permission to call store or update
* If null, permission is granted to every users
*/
protected ?string $permission = null;
public function __construct()
{
// Here I need to pass to VerifyUserPermission the controller's permission
$this->middleware(VerifyUserPermission::class)
->except(['index', 'show']);
}
}
// App\Http\Controllers\DocumentController.php
class DocumentController extends CRUDControllerBase
{
protected ?string $permission = 'documents';
}
// App\Http\Controllers\EventController.php
class EventController extends CRUDControllerBase
{
protected ?string $permission = 'events';
}
And here is how I tackled this following the #muhammad-sumon-molla-selim answer:
// App\Http\Middleware\VerifyUserPermission.php
// Create the VerifyUserPermission middleware, which accepts the permission string
class VerifyUserPermission
{
public function handle(Request $request, Closure $next, ?string $permission): mixed
{
$user = $request->user();
// If a permission is needed but the user doesn't have it, abort
if ($permission && !$user->$permissionField) {
abort(401);
}
return $next($request);
}
}
// App\Http\Kernel.php
// Register the middleware
class Kernel extends HttpKernel
{
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
// [...]
'permission' => \App\Http\Middleware\VerifyUserPermission::class
];
}
// App\Http\Controllers\CRUDControllerBase.php
// Then just register the middleware into the CRUDControllerBase's constructor
abstract class CRUDControllerBase extends Controller
{
protected ?string $permission = null;
public function __construct()
{
// 'documents' permission will be needed for Documents' edition
// 'events' permission will be needed for Events' edition
// And so on for many others controllers
$this->middleware("permission:$this->permission")
->except(['index', 'show']);
}
}
If you want to get the parameters in the __construct of your controller you could do this:
class HomeController extends \BaseController
{
public function __construct()
{
$this->routeParamters = Route::current()->parameters();
}
}

Categories