Skip to content

Commit

Permalink
more content
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Jan 9, 2023
1 parent e01db49 commit 5869d00
Show file tree
Hide file tree
Showing 24 changed files with 550 additions and 131 deletions.
3 changes: 0 additions & 3 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ index:
references:
./bin/pdg pdg:references

configuration-reference:
./bin/pdg pdg:generate-configuration pages/reference/Configuration.mdx

clean:
rm -r pages/reference/*
rm pages/guide/*.mdx
28 changes: 0 additions & 28 deletions docs/explanation/metadata.md

This file was deleted.

1 change: 1 addition & 0 deletions docs/guide/000-Declare-a-Resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// slug: declare-a-resource
// name: Declare a Resource
// position: 1
// executable: true
// ---

// # Declare a Resource
Expand Down
3 changes: 2 additions & 1 deletion docs/guide/001-Provide-the-Resource-state.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// slug: provide-the-resource-state
// name: Provide the Resource State
// position: 2
// executable: true
// ---

// # Provide the Resource State
Expand All @@ -12,7 +13,7 @@
use ApiPlatform\Metadata\ApiResource;
use App\State\BookProvider;

// We use a `BookProvider` as the [ApiResource::provider](http://localhost:3000/reference/Metadata/ApiResource#provider) option.
// We use a `BookProvider` as the [ApiResource::provider](/reference/Metadata/ApiResource#provider) option.
#[ApiResource(provider: BookProvider::class)]
class Book
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// slug: hook-a-persistence-layer-with-a-processor
// name: Hook a Persistence Layer with a Processor
// position: 2
// executable: true
// ---

// # Hook a Persistence Layer with a Processor
Expand Down
1 change: 1 addition & 0 deletions docs/guide/003-Validate-incoming-data.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// slug: use-validation-groups
// name: Use Validation Groups
// position: 3
// executable: true
// ---

// # Validing incoming data
Expand Down
57 changes: 57 additions & 0 deletions docs/guide/004-Secure-a-Resource-Access.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
// ---
// slug: secure-a-resource-access
// name: Secure a Resource Access
// position: 4
// executable: true
// ---

namespace App\ApiResource;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use App\Security\User;

// We start by securing access to this resource to logged in users.
#[ApiResource(security: "is_granted('ROLE_USER')")]
#[Get]
// To create a new resource using the Post operation, a user has to belong to the `ROLE_ADMIN` role.
// We also customize the "Access Denied." message with the `securityMessage` property.
#[Post(security: "is_granted('ROLE_ADMIN')", securityMessage: "Only an admin has access to that operation.")]
// If a user **owns** the Book or has the `ROLE_ADMIN` role, he can update the object using the Put operation. Here we're
// using the `object`'s owner. The supported variables within the access control expression are:
// - user: the current logged in object, if any
// - object: contains the value submitted by the user
// - request: the current Request object
#[Put(security: "is_granted('ROLE_ADMIN') or object.owner == user")]
#[GetCollection]
#[ORM\Entity]
class Book
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
public ?int $id = null;

#[ORM\Column]
#[Assert\NotBlank]
public string $title;

#[ORM\ManyToOne]
public User $owner;

// The security attribute is also available on [ApiProperty::security](/reference/Metadata/ApiProperty#security).
// Access control checks in the security attribute are always executed before the denormalization step.
// If you want the object after denormalization, use `securityPostDenormalize`. Using this access control variables have:
// - object: the object after denormalization
// - previous_object: a clone of the object before modifications were made
/**
* @var string Property viewable and writable only by users with ROLE_ADMIN
*/
#[ApiProperty(security: "is_granted('ROLE_ADMIN')", securityPostDenormalize: "is_granted('UPDATE', object)")]
public string $adminOnlyProperty;
}
1 change: 1 addition & 0 deletions docs/guide/006-Use-Validation-Groups.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// slug: validate-incoming-data
// name: Validate incoming Data
// position: 3
// executable: true
// ---

// ## Using Validation Groups
Expand Down
78 changes: 78 additions & 0 deletions docs/guide/012-Secure-a-Resource-with-Custom-Voters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php
// ---
// slug: secure-a-resource-with-custom-voters
// name: Secure a Resource with Custom Voters
// position: 10
// executable: true
// ---

namespace App\Security\Voter {
use App\Entity\Book;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;

// First let's create a Voter, for example using the `bin/console make:voter` command:
class BookVoter extends Voter
{
private $security = null;

public function __construct(Security $security)
{
$this->security = $security;
}

protected function supports($attribute, $subject): bool
{
// It supports several attributes related to our Resource access control.
$supportsAttribute = in_array($attribute, ['BOOK_CREATE', 'BOOK_READ', 'BOOK_EDIT', 'BOOK_DELETE']);
$supportsSubject = $subject instanceof Book;

return $supportsAttribute && $supportsSubject;
}

/**
* @param string $attribute
* @param Book $subject
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
/** ... check if the user is anonymous ... **/

switch ($attribute) {
case 'BOOK_CREATE':
if ( $this->security->isGranted(Role::ADMIN) ) { return true; } // only admins can create books
break;
case 'BOOK_READ':
/** ... other autorization rules ... **/
}

return false;
}
}
}

namespace App\ApiResource {
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;

#[ApiResource(security: "is_granted('ROLE_USER')")]
// We can then use the `is_granted` expression with our access control attributes:
#[Get(security: "is_granted('BOOK_READ', object)")]
#[Put(security: "is_granted('BOOK_EDIT', object)")]
#[Delete(security: "is_granted('BOOK_DELETE', object)")]
// On a collection, you need to [implement a Provider](provide-the-resource-state) to filter the collection manually.
#[GetCollection]
// `object` is empty uppon creation, we use `securityPostDenormalize` to get the denormalized object.
#[Post(securityPostDenormalize: "is_granted('BOOK_CREATE', object)")]
class Book
{
// ...
}
}
58 changes: 58 additions & 0 deletions docs/guide/015-extend-openapi-data.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
// ---
// slug: secure-a-resource-with-custom-voters
// name: Secure a Resource with Custom Voters
// position: 10
// executable: true
// ---

namespace App\OpenApi {
use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\OpenApi;
use ApiPlatform\OpenApi\Model;

final class OpenApiFactory implements OpenApiFactoryInterface
{
private $decorated;

public function __construct(OpenApiFactoryInterface $decorated)
{
$this->decorated = $decorated;
}

public function __invoke(array $context = []): OpenApi
{
$openApi = $this->decorated->__invoke($context);
$pathItem = $openApi->getPaths()->getPath('/api/grumpy_pizzas/{id}');
$operation = $pathItem->getGet();

$openApi->getPaths()->addPath('/api/grumpy_pizzas/{id}', $pathItem->withGet(
$operation->withParameters(array_merge(
$operation->getParameters(),
[new Model\Parameter('fields', 'query', 'Fields to remove of the output')]
))
));

$openApi = $openApi->withInfo((new Model\Info('New Title', 'v2', 'Description of my custom API'))->withExtensionProperty('info-key', 'Info value'));
$openApi = $openApi->withExtensionProperty('key', 'Custom x-key value');
$openApi = $openApi->withExtensionProperty('x-value', 'Custom x-value value');

return $openApi;
}
}
}

namespace App\Configurator {
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

function configure(ContainerConfigurator $configurator) {
$services = $configurator->services();
$services->set(App\OpenApi\OpenApiFactory::class)->decorate('api_platform.openapi.factory');
};
}

// namespace App\Tests {
// class ApiTestCase extends ApiTestCase {
//
// }
// }
5 changes: 1 addition & 4 deletions docs/guide/099-Validate-data-on-a-Delete-Operation.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// position: 99
// ---

/
// Let's add a [custom Constraint](https://symfony.com/doc/current/validation/custom_constraint.html).
namespace App\Validator {
use Symfony\Component\Validator\Constraint;
Expand All @@ -25,7 +24,7 @@ class AssertCanDeleteValidator extends ConstraintValidator
{
public function validate(mixed $value, Constraint $constraint)
{
// TODO: Implement validate() method.
/* TODO: Implement validate() method. */
}
}
}
Expand Down Expand Up @@ -81,10 +80,8 @@ public function process($data, Operation $operation, array $uriVariables = [], a
}
}


// TODO move this to reference somehow
// This operation uses a Callable as group so that you can vary the Validation according to your dataset
// new Get(validationContext: ['groups' =>])
// ## Sequential Validation Groups
// If you need to specify the order in which your validation groups must be tested against, you can use a [group sequence](http://symfony.com/doc/current/validation/sequence_provider.html).

File renamed without changes.
Empty file added docs/in-depth/events.md
Empty file.
3 changes: 3 additions & 0 deletions docs/in-depth/metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Metadata

explain metadata system, metadata factories
Loading

0 comments on commit 5869d00

Please sign in to comment.