PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Tuesday, March 8, 2022

[FIXED] Laravel Policies for nested shallow ressources

 March 08, 2022     laravel, php     No comments   

Issue

I have a the following routes defined:

//Leases
Route::resource('properties.leases', LeaseController::class)
        ->only(['show'])
        ->shallow();

//Invoices
Route::resource('leases.invoices', InvoiceController::class)
        ->only(['index', 'show'])
        ->shallow();

The above generates the following urls:

| GET|HEAD  | leases/{lease}                                             | App\Http\Controllers\LeaseController@show                                       |
| GET|HEAD  | leases/{lease}/invoices                                    | App\Http\Controllers\InvoiceController@index                                    |
| GET|HEAD  | invoices/{invoice}                                         | App\Http\Controllers\InvoiceController@show                                     |

The relationships are as below:

Properties hasMany Leases.
Leases hasMany Invoices.

I am trying to authorize these routes, so only users who:

  1. Belongs to the same team that the "Leases" and "Invoices" also belong to.
  2. Is currently logged in on that team.

In my AuthServiceProvider I have defined the following policies:

protected $policies = [
    Lease::class => LeasePolicy::class,
    Invoice::class => InvoicePolicy::class,
];

In my LeaseController I have defined the authorization check:

public function __construct()
{
    $this->authorizeResource(Lease::class, 'lease');
}

The LeasePolicy looks like this:

public function view(User $user, Lease $lease)
{
    //Does the current user belong to the team that the lease is associated with
    //and is the user's current team the same one?
    $team = $lease->property->team;
    return $user->belongsToTeam($team) && $user->isCurrentTeam($team);
}

And in my InvoiceController I have defined this:

public function __construct()
{
    $this->authorizeResource(Invoice::class, 'invoice');
}

The InvoicePolicy looks like this:

/**
 * Path: leases/{lease}/{$invoice}
 */
public function viewAny(User $user)
{

    //When users go to this path I can only access $user here. 
    //How to check if the user can even access the $lease.

}

/**
 * Path: invoices/{$invoice}
 */
public function view(User $user, Invoice $invoice)
{
    //Does the current user belong to the team that the lease is associated with
    //and is the user's current team the same one?
    $team = $invoice->lease->property->team;
    return $user->belongsToTeam($team) && $user->isCurrentTeam($team);
}

In my application, I have a lot of routes that are "under" the /lease/{lease}/{model} route, e.g.:

//Files
Route::resource('leases.files', FileController::class)
        ->only(['index'])
        ->shallow();

For these, how can I define my Policies so only users who are allowed to view these ressources can get access?


Solution

As far as I know at the time I wrote this answer, the authorizeResource cannot be used on some shallow nested methods (such as the index, create, & store). So instead, you can call authorize function on each method via controller helpers.

Or if you still want to use authorizeResource, you can only call authorize manually on some shallow nested methods like the following example:

class InvoiceController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Invoice::class, 'invoice');
    }

    /**
     * Get the map of resource methods to ability names.
     *
     * @return array
     */
    protected function resourceAbilityMap()
    {
        return collect(parent::resourceAbilityMap())
            ->except(['index', 'create', 'store'])
            ->all();
    }

    /**
     * Display a listing of the resource.
     *
     * @param \App\Models\Lease $lease
     * @return \Illuminate\Http\Response
     */
    public function index(Lease $lease): Response
    {
        $this->authorize('view', $lease);

        ...
    }
}

And by the way, if you want to make InvoicePolicy simpler, you can also reuse LeasePolicy like the following code:

class InvoicePolicy
{
    use HandlesAuthorization;

    public function __construct(
        protected LeasePolicy $leasePolicy,
    ) {
    }

    /**
     * Determine whether the user can view the model.
     *
     * @param  \App\Models\User  $user
     * @param  \App\Models\Invoice  $invoice
     * @return \Illuminate\Auth\Access\Response|bool
     */
    public function view(User $user, Invoice $invoice): bool
    {
        return $this->leasePolicy->view($user, $invoice->lease);
    }
}


Answered By - Yusuf T
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing