Laravel: запретить изменение элементов других пользователей - PullRequest
0 голосов
/ 30 марта 2020

У меня три модели. Я хочу, чтобы пользователи не могли изменять задачи из списков задач, принадлежащих другим пользователям.

class User extends Authenticatable
{
    public function todolists()
    {
        return $this->hasMany('App\Todolist');
    }

    public function todos()
    {
        return $this->hasManyThrough('App\Todo', 'App\Todolist');
    }
}

class Todolist extends Model
{
    public function user()
    {
        return $this->belongsTo('App\User');
    }

    public function todos()
    {
        return $this->hasMany('App\Todo');
    }
}

class Todo extends Model
{
    protected $casts = [
        'completed' => 'boolean',
    ];

    public function todolist()
    {
        return $this->belongsTo('App\Todolist');
    }
}

Чтобы пользователи не могли просматривать списки задач и элементы других пользователей, я реализовал следующее:

 public function getTodosForTodolist(Todolist $todolist)
    {
        if (Auth::user()->id == $todolist->user_id) {
            $todos = Todo::where('todolist_id', $todolist->id )->get();
            return view('todo/index', ['todos' => $todos); 
        }
        else {
            abort(403, 'Unauthorized action.');
        }
    }

Следующий шаг - запретить пользователям редактировать элементы задач других пользователей. В настоящее время в TodoController у меня просто следующее:

public function edit(Todo $todo)
        {
         if (Auth::user()->todos->id == $todo->todolist->id) {
            return view('todo/edit', ['todo' => $todo]);
         }
    } 

Это дает следующую ошибку:

Свойство [id] не существует в этом экземпляре коллекции.

Ошибка в том, что текущий пользователь имеет несколько задач. Поэтому я изменил свой код следующим образом.

public function edit(Todo $todo)
    {
        if (Auth::user()->todos->first()->id == $todo->todolist->id) {
            return view('todo/edit', ['todo' => $todo]);
        }
        abort('403', 'Unauthorized action.'); 
}

Это работает, но мне кажется, что делать это как-то неправильно.

Что может быть лучше для sh, чтобы пользователи могли просматривать / редактировать / удалять элементы, принадлежащие другим пользователям?

Ответы [ 2 ]

2 голосов
/ 30 марта 2020

Я предлагаю вам использовать политики для моделей Todo и TodoList, а также область действия, чтобы ограничить задачи одним пользователем для предотвращения дублирования кода в вашем приложении:

class ToDoListPolicy
{
    public function view(User $user, TodoList $post)
    {
        return $user->id === $todolist->user_id;
    }
}

class ToDoPolicy
{
    public function edit(User $user, Todo $toDo)
    {
        $toDo->loadMissing('todolist');

        return $user->id === $toDo->todolist->user_id;
    }
}

Зарегистрируйте их в ваш AuthServiceProvider. php

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        TodoList::class => ToDoListPolicy::class,
        Todo::class => ToDoPolicy::class
    ];
}

и затем используйте их в своих действиях:

public function getTodosForTodolist(Todolist $toDoList)
{
    $this->authorize('view', $toDoList);

    $toDoList->loadMissing('todos');

    return view('todo.index', ['todos' => $toDoList->todos); 
}

class ToDoController extends Controller
{
    public function edit(Todo $toDo)
    {
        $this->authorize('edit', $toDo);

        return view('todo.edit', compact('toDo'));
    } 
}

И область действия, чтобы ограничить запрос указанным c user:

class Todo extends Model {
    // ...

    public function scopeByUser(Builder $query, ?User $user = null)
    {
        if (! $user) {
            $user = Auth::user();
        }

        $query->whereHas('todolist', function (Builder $toDoListQuery) use ($user) {
            $toDoListQuery->where('user_id', $user->id);
        });
    }
}

Ответ на ваши вопросы в комментариях.

Q1: Я должен был поставить Auth::user()->can('view', $todolist); в if Еще одна оговорка, чтобы это работало. Угадайте, как это работает?

Q2: в чем разница между $this->authorize('edit', $todo) и Auth::user()->can('edit', $todo)?

Извините, это была ошибка на моей стороне. Auth::user()->can() возвращает логическое значение, тогда как $this->authorize() (который является методом черты AuthorizesRequests, обычно включаемой в BaseController) выдает исключение , если авторизация не удалась .

1 голос
/ 30 марта 2020

Если вы хотите, чтобы каждый пользователь работал только со своими собственными Todos, то вам нужно добавить Global Scope. Эта реализация позволит вашему приложению почувствовать, что Todos (кроме зарегистрированных пользователей) не существует.

Глобальные области видимости могут использоваться для многих моделей, что означает, что он уменьшит код котельной пластины.

https://laravel.com/docs/7.x/eloquent#global -scopes

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...