getting infinite loop - adding scope on user model with traits - laravel

after adding the following trait to the user model, I get a 500 error because of an infinite loop.
trait Multitenantable
{
public static function bootMultitenantable()
{
static::addGlobalScope('tenant_id', function (Builder $builder) {
$tenant_id = 1;
if ( auth()->check() )
{
$tenant_id = Auth::user()->tenant_id;
}
$builder->where('tenant_id', '=', $tenant_id);
});
}
}
when I remove either the trait from the user model or the if containing the auth() part (lines 7,8,9,10) from this trait, the infinite loop resolves.
I'm not familiar with the magic behind laravel, could someone explain why this happens?
and how I could add global scope for multitenancy to the user model like the others?
I've followed this instruction to add multitenancy to my laraval project.

I resolve this by creating trait and used the trait on user model
use Illuminate\Database\Eloquent\Builder;
trait FilterByClass
{
protected static function booted()
{
if( request()->session()->exists('auth')){
$user = auth()->user();
self::addGlobalScope(function(Builder $builder)use($user){
if($user->hasRole('Head Teacher')){
return;
}
$builder->where('branch_id',$user->class_id);
});
}
}
}
I figured the loop happens because you are trying to authenticate from the user model and at the same time the model is looking for an authenticated user hence use are deadlocked.
so by checking if an request()->session()->exist('auth') exist before applying the scope will make sure it check for an auth()->user() only if it exist.

Apparently when I add the auth condition to this trait, I cannot add the trait to user model.
If I do, when the user model is booting, It must check the Auth, wich requires booting the user model. And this cause the infinite loop.

Related

How to used Tucker-Eric/EloquentFilter Laravel

good day, I am using Tucker-Eric/EloquentFilter Laravel.
I want to filter it by relationship using Models
I want to automate it, instead of using the following:
public function users($users)
{
// dd($users);
return $this->r('users', $users);
}
public function user($user)
{
// dd($user);
return $this->r('user', $user);
}
public function owner($owner)
{
// dd($owner);
return $this->r('owner', $owner);
}
I want to make it one function that based on the relationship
so that I want to add another relationship on the model I don't need anymore to add another function.
Thanks!
We specifically stayed away from the type of implicit functionality you're looking for and opted for explicit filter methods to avoid security issues if/when new relations/properties were added to a model they wouldn't implicitly be available to filter against.
With that, what you're looking for isn't recommended because of the security concerns above but it can still exist if you implement it.
It sounds like the setup method would be the best place to implement it since it would be called first every time ->filter() is called.
public function setup()
{
foreach($this->input() as $key => $val) {
if($this->getModel()->$key() instanceof \Illuminate\Database\Eloquent\Relations\Relation) {
// Your logic here
}
}
}

How to use custom keys in laravel routes without scoping?

When using custom keys Laravel forces us with scoping, for example, I have a route to getting a country and a post
api/countries/{country:slug}/posts/{post:slug}
but I can't get that using slug key because it doesn't have a relation with country, and in this case, I want to handle scope myself and I don't need implicitly scope binding, but I get an error (Call to undefined method App\Country::posts() ).
so because of that I cant using this Laravel feature. is there a way to turn the implicitly scope binding off?
If the posts are not related to the countries, it may not make sense to nest them in the URI?
But, nonetheless, to answer your question, you need to do one of two things:
Instead of setting {country:slug}, just use {country} and then override getKeyRouteName() function on your Country and Post models.
Alternatively, especially if you want to use the ID elsewhere, use explicit model binding.
To use a slug without custom keys in the routes file
class Post
{
[...]
public function getRouteKeyName()
{
return 'slug';
}
}
To use explicit route model binding
Add the following to the boot() method of your RouteServiceProvider:
public function boot()
{
parent::boot();
Route::bind('post', function ($value) {
return App\Post::where('slug', $value)->firstOrFail();
});
}

How to define policy for a list or array in laravel?

I have the following policy which determines if a user is able to view a contract.
public function view(User $user, Contract $contract)
{
if ($user->user_type->id == 2) { // If user is a vecino
if ($user->id == $contract->customer_id) {
return true;
}
} else if ($user->user_type->is_admin == true) { // If user is an admin
return true;
}
return false;
}
Which is then checked for authorization with
$this->authorize('view', $contract);
How do I check authorization for a list/array/collection? Like if I get a list of contracts via Contract::all()
I haven't found any way to do this. I could do a loop and call $this->authorize for every iteration to check for authorization but that might impact performance.
Is there a better way of doing this?
One solution I am currently using is a hybrid approach where you define your rules within a scope and then reference that scope from the policy allowing you to reuse your authorization logic.
// Contract model
public function scopeViewable($query)
{
// If the user is admin, just return the query unfiltered.
if (Auth::user()->user_type->is_admin) {
return $query;
}
// Check the contract belongs to the logged in user.
return $query->where('customer_id', Auth::id());
}
And then in your policy, reference that scope but restrict it to the current model. Make sure to return a boolean using exists(). This essentially checks that your model is viewable.
// Contract Policy
public function view(User $user, Contract $contract)
{
return Contract::viewable()
->where('id', $contract->id)
->exists()
;
}
Importantly, you should use the scope when retrieving a collection of models and not the policy which would run the scope query for each model in the collection. Policies should be used on individual model instances.
Contract::viewable()->paginate(10);
// Or
Contract::viewable()->get();
But, when you want to check an individual contract you can use your policy directly.
$this->authorize('view', $contract);
// Or
Auth::user()->can('view', [Contract::class, $contract]);
The design i often sees in this case, is to check if all elements in the query is allowed to be viewed through the policy. This does not scale well and works bad with pagination.
Instead of filtering out the contracts with policies, the better solution is to filter the contracts already in the query. This mainly because if you want to do pagination down the line, you want to do all filtering before the query is executed to avoid having weird pagination meta data. While also having to run n operations for each element, which would already be a problem at 1000 elements.
There for doing the following query clause, can obtain the same result as your policy.
Contract::where('user_id', $user->id)->get();
A version of this i usually do to make things easier for my self is creating a scope in the user model.
public function scopeOwned($query, User $user)
{
return $this->query->where('user_id', $user->id);
}
Contract::owned($user)->get();
You have to loop, one way to another. There is no difference between looping over Contract object in your controller, or on your policy, but policies are made to check a single resource so I would do that in your controller.

How to Override Update Method in Laravel 5.8

I'm trying to create a ticketing system that's linked to timesheets. Whenever someone updates a ticket, they have the option of submitting how much time has been spent on it, in a time_spent form object. Timesheets are polymorphically linked to many objects.
I want to create a trait, CreatesTimesheets, then apply that to relevant models so that:
Each of those models gets a timesheets() function.
It overrides the update() method of each model that it's a trait of, to check whether any time was submitted in time_spent.
It's the second bit that isn't working. My code is as below, and when I update the model (which works fine), this code doesn't fire at all, even testing it with a simple dd().
How do I fix this?
<?php
namespace App\Traits;
use App\Models\HR\Timesheet;
use Auth;
trait CreatesTimesheets
{
public function update(array $attributes = [], array $options = [])
{
dd('test');
if ($request->time_spent)
{
$timesheet = new Timesheet;
$timesheet->time_logged_in_mins = $request->time_spent;
$timesheet->appointment_id = Auth::user()->appointedJobIDToUse();
$this->timesheets()->save($timesheet);
}
parent::update($attributes, $options);
}
public function timesheets()
{
return $this->morphToMany('App\Models\HR\Timesheet', 'timesheetable');
}
}

Models accessible only for authenticated user THAT CREATED THEM (Laravel)

I'm writing a software application to let the people have their own private archive of cooking recipes.
The RecipeController constructor contains:
$this->middleware('auth')
because only registered users can use recipes, but I need to protect also the access to the models.
The point is that users can view and modify only their own recipes.
Example: The user TortelliEngineer can create a recipe "Tortelli Secret Recipe" using the model Recipe; he can view, update and delete his recipe(s), but nobody else can see his precious "Tortelli Secret Recipe".
So, which is the cleanest way?
I added a user_id attribute to the model Recipe.
I must use this parameter every single time that I ask to the database for a Recipe (goodbye "findOrFail" by ID)
That means that every time I make a request I must access the Request object that contains User that contains User_id
using Auth::id() EVERY SINGLE TIME that I need one (or n) recipe
Like this:
class RecipeRepository{
public function all(){
return Recipe::where('user_id', Auth::id())
->orderBy('created_at', 'asc')
->get();
}
public function find($recipe_id){
return Recipe::where('user_id', Auth::id())
->where('id', $recipe_id)
->firstOrFail();
}
Is that correct? Do you hate me for this? Do you know better or more correct ways to do it?
Most of the time I make a method inside the model to check if someone is authorised, owner etc.. of something.
An example would be:
// User model
public function owns_recipe($recipe)
{
return ($recipe->user_id == $this->id);
}
You can call this at the very beginning in of the methods of your controller:
// Controller
public function index (Request $request)
{
$recipe = Recipe::find($request->id); // Get recipe
$user = ... // Get user somehow
if (!$recipe) App::abort(404); // Show 404 not found, or something
if (!$user->owns_recipe($recipe)) App::abort(403); // Show 403 permission denied, or something
... // Do whatever you want :)
}
While there are many ways of approaching this, Laravel does provide some built-in methods for handling general authentication of actions. In the first place I'd do something along the lines of what you intended (have a getRecipesByOwner method in RecipeRepository) and you can pass the user to it from the injected Request object:
// RecipeController
public function index(Request $request)
{
$recipes = $this->recipeRepo->findRecipesByOwner($request->user());
}
In addition though, I'd recommend creating policies to manage whether or not a user is capable of updating/deleting/viewing individual recipes. You can then authorize their actions in the controllers/blade templates/etc. via built-in methods like:
// Controller
public function update(Request $request, Recipe $recipe)
{
$this->authorize('update', $recipe);
}
// Blade template
#can('update', $recipe)
#endcan
The documentation is available at: https://laravel.com/docs/5.3/authorization#creating-policies

Resources