Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authorizer runs before Validator, exposes runtime error risk #121

Open
GregPeden opened this issue May 31, 2017 · 0 comments
Open

Authorizer runs before Validator, exposes runtime error risk #121

GregPeden opened this issue May 31, 2017 · 0 comments

Comments

@GregPeden
Copy link

I THINK, per this experience, that the validation test relationshipRules() is run AFTER the authorizer is run. That might be okay, it just means that the authorizer logic has to assume that required attributes or relationships might not be present or may be set to null (meaning 'no relationship'). This seems undesirable, but maybe it's important, in which case this should be well documented.

Example below, where setting the relationship "project" to null causes a fatal error "Call to a member function getId() on null in /home/vagrant/Code/hoistway-cms/app/JsonApi/Tasks/Authorizer.php:59". I can design around this by checking first, or it would be fixed if validation were performed first. Maybe there is a reason to not do that, though? Note that 'TemplateAuthorizer' is my own common auth check class, and for Tasks it has a special case so I am only overwriting the relevant test.

<?php

namespace App\JsonApi\Tasks;

use App\JsonApi\TemplateValidators;
use App\Models\Task\Task;
use CloudCreativity\JsonApi\Contracts\Validators\RelationshipsValidatorInterface;

class Validators extends TemplateValidators
{
    /**
     * @var string
     */
    protected $resourceType = 'tasks';

    /**
     * @var string[]
     */
    protected $allowedIncludePaths = [
        'project',
        'task-type'
    ];

    /**
     * @var array
     */
    protected $allowedSortParameters = [
        'created_at',
        'updated_at',
        'name',
        'slug'
    ];

    /**
     * @var array
     */
    protected $allowedFilteringParameters = [
        'id',
        'name',
        'slug'
    ];

    /**
     * Get the validation rules for the resource attributes.
     *
     * @param Task $record
     *      the record being updated, or null if it is a create request.
     * @return array
     */
    protected function attributeRules($record = null)
    {
        return [
            'name' => ['required', 'string', 'between:3,96'],
        ];
    }

    /**
     * Define the validation rules for the resource relationships.
     *
     * @param RelationshipsValidatorInterface $relationships
     * @param object|null $record
     *      the record being updated, or null if it is a create request.
     * @return void
     */
    protected function relationshipRules(RelationshipsValidatorInterface $relationships, $record = null)
    {
        $relationships->hasOne('project', 'projects', is_null($record), false);
        $relationships->hasOne('task-type', 'task-types', is_null($record), false);
    }

}
<?php

namespace App\JsonApi\Tasks;

use App\JsonApi\TemplateAuthorizer;
use App\Models\Project\Project;
use App\Models\Task\Task;
use CloudCreativity\JsonApi\Contracts\Object\ResourceInterface;
use Gate;
use Neomerx\JsonApi\Contracts\Encoder\Parameters\EncodingParametersInterface;

class Authorizer extends TemplateAuthorizer
{

    /**
     * Return the Eloquent model to be used for policy checks
     *
     * @return string
     */
    protected function getPolicyModel()
    {
        return Task::class;
    }

    /**
     * Can the client read many resources at once?
     *
     * Encoding parameters are provided in case the parameters such as
     * filtering or inclusion paths affect whether the resources can be read.
     *
     * @param string $resourceType
     *      the requested resource type.
     * @param EncodingParametersInterface $parameters
     *      the parameters provided by the client
     * @return bool
     */
    public function canReadMany($resourceType, EncodingParametersInterface $parameters)
    {
        return false;
    }

    /**
     * Can the client create the provided resource?
     *
     * @param string $resourceType
     *      the resource type being created.
     * @param ResourceInterface $resource
     *      the resource provided by the client.
     * @param EncodingParametersInterface $parameters
     *      the parameters provided by the client
     * @return bool
     */
    public function canCreate($resourceType, ResourceInterface $resource, EncodingParametersInterface $parameters)
    {
        if (!$resource->getRelationships()->has('project')) {
            return false;
        }

        $projectId = $resource->getRelationships()->getRelationship('project')->getData()->getId();
        $project = Project::find($projectId);

        return Gate::allows('update', $project);
    }
}

Example of bad JSON package which cases server-side error:

data: {
  attributes: {
    name: 'Test'
  },
  relationships: {
    project: {
      data: null,
    },
    'task-type': {
      data: {
        type: 'task-types',
        id: '1'
      }
    }
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant