dwiarfian's avatar

[SOLVED] This action is unauthorized

Hi. I'm using Policy for all of the models. All is fine until I add policy to my ProjectData model. The index and store function are fine, but the other don't work (This action is unauthorized). I tried to set the return to true but still same error message.

Here are my codes.

ProjectDataController:

<?php

namespace App\Http\Controllers\Api\v1;

use App\Http\Controllers\Controller;
use App\Http\Requests\ProjectData\StoreProjectDataRequest;
use App\Http\Requests\ProjectData\UpdateProjectDataRequest;
use App\Models\ProjectData;
use App\Utils\JsendFormatter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;
use Str;

class ProjectDataController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(ProjectData::class, 'project_data');
    }

    public function index(Request $request): JsonResponse
    {
        $projectData = QueryBuilder::for(ProjectData::class)
            ->allowedFilters(['value', AllowedFilter::exact('team_id'), AllowedFilter::exact('project_data_scheme_id')])
            ->allowedSorts(['id', 'team_id', 'project_data_scheme_id', 'created_at', 'updated_at'])
            ->paginate($request->query('per_page', 10));

        return JsendFormatter::success_paginated('projectData', $projectData);
    }

    public function show(ProjectData $projectData): JsonResponse
    {
        return JsendFormatter::success_singleton('projectData', $projectData);
    }

    public function store(StoreProjectDataRequest $request): JsonResponse
    {
        if ($request->hasFile('value')) {
            $disk = 'local';
            $path = 'project_data';
            $file = $request->file('value');
            $file_name = Str::ulid()->toRfc4122() . '.' . $file->extension();

            $file->storeAs($path, $file_name, $disk);

            $projectData = ProjectData::create(
                array_replace(
                    $request->validated(),
                    ['value' => implode(';', [$disk, $path, $file_name])]
                )
            );
        } else {
            $projectData = ProjectData::create($request->validated());
        }

        return JsendFormatter::success_singleton('projectData', $projectData, 201);
    }

    public function update(UpdateProjectDataRequest $request, ProjectData $projectData): JsonResponse
    {
        if ($request->hasFile('value')) {
            $disk = 'local';
            $path = 'project_data';
            $file = $request->file('value');
            $file_name = Str::ulid()->toRfc4122() . '.' . $file->extension();

            $file->storeAs($path, $file_name, $disk);

            $projectData->update(
                array_replace(
                    $request->validated(),
                    ['value' => implode(';', [$disk, $path, $file_name])]
                )
            );
        } else {
            $projectData->update($request->validated());
        }

        return JsendFormatter::success_singleton('projectData', $projectData);
    }

    public function destroy(ProjectData $projectData): JsonResponse
    {
        $projectData->delete();

        return JsendFormatter::success_singleton('projectData', $projectData);
    }
}

ProjectDataPolicy:

<?php

namespace App\Policies;

use App\Models\ProjectData;
use App\Models\User;

class ProjectDataPolicy
{
    public function viewAny(User $user): bool
    {
        return true;
    }

    public function view(User $user, ProjectData $projectData): bool
    {
        return $user->is_admin || $projectData->team->reviewers->contains($user->id) || $user->id == $projectData->team->leader_id || $user->teams->contains($projectData->team_id);
    }

    public function create(User $user): bool
    {
        return ! $user->is_reviewer;
    }

    public function update(User $user, ProjectData $projectData): bool
    {
        return $user->is_admin || $user->id == $projectData->team->leader_id || $user->teams->contains($projectData->team_id);
    }

    public function delete(User $user, ProjectData $projectData): bool
    {
        return $user->is_admin || $user->teams->contains($projectData->team_id);
    }
}

ProjectData Model:

<?php

namespace App\Models;

use DateTimeInterface;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class ProjectData extends Model
{
    use HasFactory;

    protected $fillable = ['team_id', 'project_data_scheme_id', 'value'];

    protected $dateFormat = DATE_ATOM;

    protected function serializeDate(DateTimeInterface $date): string
    {
        return $date->format(DATE_ATOM);
    }

    public function team()
    {
        return $this->belongsTo(Team::class);
    }

    public function projectDataScheme()
    {
        return $this->belongsTo(ProjectDataScheme::class);
    }
}
0 likes
11 replies
lazerg's avatar

Hi, it is because index and store does not need any parameter. But update, show and destroy needs $projectData model to pass.

Instead of writing in constructor, write in each method this resource

for example for index:

$this->authorizeResource(ProjectData::class, 'project_data');

but for show method:

$this->authorizeResource($projectData);

i am not sure about arguments i passed is correct, but the idea is like this..

2 likes
dwiarfian's avatar

@lazergI tried it before and it works. But why does it happened? I write same code to all of my controllers and they work fine and with no problem.

dwiarfian's avatar

@vincent15000

Route::middleware(['auth:sanctum'])->group(function () {
        Route::get('me', function (Request $request) {
            return JsendFormatter::success_singleton('user', $request->user());
        });

        Route::apiResources([
            'announcements' => AnnouncementController::class,
            'batches' => BatchController::class,
            'divisions' => DivisionController::class,
            'ideas' => IdeaController::class,
            'project-data' => ProjectDataController::class,
            'project-data-schemes' => ProjectDataSchemeController::class,
            'proposals' => ProposalController::class,
            'reviews' => ReviewController::class,
            'scorings' => ScoringController::class,
            'scoring-schemes' => ScoringSchemeController::class,
            'teams' => TeamController::class,
            'team-members' => TeamMemberController::class,
            'team-reviewers' => TeamReviewerController::class,
            'timelines' => TimelineController::class,
            'users' => UserController::class,
            'years' => YearController::class,
        ]);
    });
1 like
dwiarfian's avatar

@vincent15000

 GET|HEAD        api/v1/project-data ................................................. project-data.index ??? Api\v1\ProjectDataController@index
  POST            api/v1/project-data ................................................. project-data.store ??? Api\v1\ProjectDataController@store
  GET|HEAD        api/v1/project-data-schemes ........................... project-data-schemes.index ??? Api\v1\ProjectDataSchemeController@index
  POST            api/v1/project-data-schemes ........................... project-data-schemes.store ??? Api\v1\ProjectDataSchemeController@store
  GET|HEAD        api/v1/project-data-schemes/{project_data_scheme} ....... project-data-schemes.show ??? Api\v1\ProjectDataSchemeController@show
  PUT|PATCH       api/v1/project-data-schemes/{project_data_scheme} ... project-data-schemes.update ??? Api\v1\ProjectDataSchemeController@update
  DELETE          api/v1/project-data-schemes/{project_data_scheme} . project-data-schemes.destroy ??? Api\v1\ProjectDataSchemeController@destroy
  GET|HEAD        api/v1/project-data/{project_datum} ................................... project-data.show ??? Api\v1\ProjectDataController@show
  PUT|PATCH       api/v1/project-data/{project_datum} ............................... project-data.update ??? Api\v1\ProjectDataController@update
  DELETE          api/v1/project-data/{project_datum} ............................. project-data.destroy ??? Api\v1\ProjectDataController@destroy
1 like
vincent15000's avatar
Level 63

@dwiarfian Here you can see that the variable is automatically named project_datum by Laravel.

So in your controller you have to name it exactly the same.

$this->authorizeResource(ProjectData::class, 'project_datum');

...

public function update(UpdateProjectDataRequest $request, ProjectData $project_datum): JsonResponse

Otherwise you can write all the routes manually instead of using apiResources.

1 like

Please or to participate in this conversation.