Spatie's wildcard permission or not?

175 views Asked by At

I am currently building a job portal application using laravel 10.x. There will be two types of user roles who will be using the application:

  • Employer
  • Candidate

For permissions, I am using [spatie's permission package][1].

As per the requirements of the project, Employers can create jobs. Candidates can see all jobs but the employer will only be able to see the job he has created.

I have two approaches to build the above restriction:

  • Using [Spatie's wildcard permissions][1] to achieve this where I can add jobs.*.{user_id} so that the permission is added in the spatie module and I have all permission related logic at one place

when the job is created, I will write the code to give permission to the employer to the job by doing:

$employer->givePermissionTo('jobs.*.'.$job_id)

and then validate whether the employer has the permission to view the job by checking

$user->can('job.view');

OR

  • Have a middleware to check the user type is employer and the one who created this job matches the user_id of the currently logged in user?

    
    if($user->type == 'employer' && $job->employer_user_id != Auth::user()->id || $user->can('job.view');)
        abort('You do not have access to this job');
    
    

Which approach is the ideal solution?

1

There are 1 answers

0
Lance Armah-Abraham On

Yes wildcard permissions can work perfectly. I think the middleware route makes it easier because you just have to remember to add the middleware to the routes. You can combine it with what you wanted to do. But if you give permission to an employer, I am not sure if the employer model is the same as the user model because if you give permission to the employer with $employer->givePermissionTo('jobs.*.'.$job_id), doing $user->can('job.view'); does not check that the employer can view the job, if the employer is the same as the user, then you can check the permission instead with $user->can('job.view.1'), where "1" is the job id.

As an alternative, you can also try this.

You can consider using model policies for this use case.

Say, you have a Job model policy, JobPolicy

  1. Create a candidate and employer role.
  2. Create a view any jobs permission to both candidate and employer role. This will allow both employer and candidate to view any jobs which should be ok as they both need to be able to view all jobs**. For the employer, what you can do is say, in the index method or the method where you list all jobs for the employer, you can filter with something like Job::where('user_id', auth()->id()) to make sure the employer only sees jobs they created.
  3. In the method to viewAny in your JobPolicy, you can have a logic like:
/**
 * Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
     // allow if the user can view any jobs
     if ($user->can('view any jobs')) {
         return true;
     }
   
}
  1. In the method to view in your JobPolicy, you can then have a logic that only allows an employer to view a job if they own the job.
/**
 * Determine whether the user can view any models.
*/
public function view(User $user, Job $job): bool
{
     // allow candidates through
     if ($user->hasRole('candidate')) {
         return true;
     }
     // allow the employer if they own the job
     if ($user->id === $job->id) {
         return true;
     }
}