@shahruslan
Веб-программист

Laravel. Как правильно сделать авторизацию действий в сервисе?

Есть сущность проекты. У проекта есть свойство со списком пользователей, которые работают с этим проектом.
Соответственно есть такие операции(permissions):
1. projects.view - просмотр списка проектов, в которых ты участвуешь.
2. projects.users.view - просмотр списка участников проекта
3. projects.users.edit - редактирование списка участников проекта

Получается, пользователи, имеющие permission projects.view, видят список проектов, в которых они участвуют.
Некоторые из пользователей, имеющие permission projects.users.view, могут видеть еще и всех участников этого проекта.

Вот роут:
$options = ['only' => ['index', 'store', 'destroy']];
Route::resource('/projects/{projectId}/users', 'ProjectUsersController', $options)->names('projects.users');

Можем посмотреть список пользователей, добавить пользователя в проект и удалить из проекта.

Вот как контроллер выглядит сейчас
class ProjectUsersController extends Controller
{
    public function index(ProfileRepository $profileRepository, $projectId)
    {
        $this->authorize('projects.users.view');

        $project = Project::find($projectId);

        $this->authorize('access', $project);

        $userIds = $project->users()->select('id')->pluck('id')->toArray();
        $users = $profileRepository->getByIds($userIds);

        return $users;
    }
}


На всякий случай скину "политику" для операции access
class ProjectPolicy
{
    use HandlesAuthorization;

    public function access(User $user, Project $project)
    {
        if ($user->can('projects.view') == false) {
            return false;
        }

        if ($user->id == $project->created_by) {
            return true;
        }

        if ($project->users()->pluck('id')->contains($user->id)) {
            return true;
        }

        return false;
    }
}


Вот собственно вопрос. Хочу вынести "получение списка пользователей проекта" в отдельный сервис. И в контроллере вызвать этот сервис, например ProjectUsersService::getUsers($projectId). Операцию projects.users.view я могу проверить прямо в контроллере. А операцию access могу проверить только после получения объекта проекта. Просто не хотелось бы получать проект вне сервиса:
$this->authorize('projects.users.view');
$project = Project::find($projectId);
$this->authorize('access', $project);
$projectUsersService->getUsers($project);

// далее еще будут добавление и удаление участников
$this->authorize('projects.users.edit');
$project = Project::find($projectId);
$this->authorize('access', $project);
$projectUsersService->addUser($project, $userId);


А авторизация внутри сервиса не всегда нужна. Получается нужно в метод getUsers/addUser/removeUser нужно добавлять флаг, о том делать проверку или нет. Или можно как-то еще?

UPD
Вот код сервиса, который получается, если проверку операции access добавить в сервис
class ProjectUsersService
{
    public function getUsers(ProfileRepository $profileRepository, int $projectId)
    {
        $project = Project::find($projectId);
        
        if (\Auth::user()->can('access', $project) == false) {
            throw new \Exception();
        }
        
        $userIds = $project->users()->select('id')->pluck('id')->toArray();
        $users = $profileRepository->getByIds($userIds);

        return $users;
    }
    
    public function addUserInProject(int $projectId, int $userId)
    {
        $project = Project::find($projectId);
        
        if (\Auth::user()->can('access', $project) == false) {
            throw new \Exception();
        }
        
        $project->users()->attach($userId);
    }

    public function deleteUserFromProject(int $projectId, int $userId)
    {
        $project = Project::find($projectId);
        
        if (\Auth::user()->can('access', $project) == false) {
            throw new \Exception();
        }
        
        $project->users()->detach($userId);
    }
}
  • Вопрос задан
  • 92 просмотра
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы