feature: add Authenticator
This commit is contained in:
parent
b102519154
commit
2380c13c37
14
.idea/php-test-framework.xml
Normal file
14
.idea/php-test-framework.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PhpTestFrameworkVersionCache">
|
||||
<tools_cache>
|
||||
<tool tool_name="PHPUnit">
|
||||
<cache>
|
||||
<versions>
|
||||
<info id="LocalC:\Users\jklattenhoff\Documents\symfony_crud\vendor\autoload.php" version="10.5.7" />
|
||||
</versions>
|
||||
</cache>
|
||||
</tool>
|
||||
</tools_cache>
|
||||
</component>
|
||||
</project>
|
@ -89,6 +89,14 @@
|
||||
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
|
||||
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
|
||||
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/serializer" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/property-info" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/property-access" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpstan/phpdoc-parser" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-docblock" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/type-resolver" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-common" />
|
||||
<path value="$PROJECT_DIR$/vendor/webmozart/assert" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpInterpreters">
|
||||
|
@ -11,6 +11,9 @@
|
||||
<PhpSpecSuiteConfiguration>
|
||||
<option name="myPath" value="$PROJECT_DIR$" />
|
||||
</PhpSpecSuiteConfiguration>
|
||||
<PhpSpecSuiteConfiguration>
|
||||
<option name="myPath" value="$PROJECT_DIR$" />
|
||||
</PhpSpecSuiteConfiguration>
|
||||
</suites>
|
||||
</component>
|
||||
</project>
|
@ -26,6 +26,10 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/php-parser" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/type-resolver" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpstan/phpdoc-parser" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-invoker" />
|
||||
@ -74,8 +78,11 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php83" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/process" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/property-access" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/property-info" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/routing" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/runtime" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/serializer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/stopwatch" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/string" />
|
||||
@ -83,6 +90,7 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-exporter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/yaml" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/theseer/tokenizer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
@ -10,11 +10,17 @@
|
||||
"doctrine/doctrine-bundle": "^2.11",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.3",
|
||||
"doctrine/orm": "^2.17",
|
||||
"phpdocumentor/reflection-docblock": "^5.3",
|
||||
"phpstan/phpdoc-parser": "^1.25",
|
||||
"symfony/console": "7.0.*",
|
||||
"symfony/dotenv": "7.0.*",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/framework-bundle": "7.0.*",
|
||||
"symfony/property-access": "7.0.*",
|
||||
"symfony/property-info": "7.0.*",
|
||||
"symfony/runtime": "7.0.*",
|
||||
"symfony/security-bundle": "7.0.*",
|
||||
"symfony/serializer": "7.0.*",
|
||||
"symfony/yaml": "7.0.*"
|
||||
},
|
||||
"config": {
|
||||
|
1072
composer.lock
generated
1072
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,4 +5,5 @@ return [
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
];
|
||||
|
40
config/packages/security.yaml
Normal file
40
config/packages/security.yaml
Normal file
@ -0,0 +1,40 @@
|
||||
security:
|
||||
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
|
||||
password_hashers:
|
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
||||
providers:
|
||||
users_in_memory: { memory: null }
|
||||
firewalls:
|
||||
dev:
|
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
main:
|
||||
lazy: true
|
||||
provider: users_in_memory
|
||||
custom_authenticator: App\Security\PrinterAuthenticator
|
||||
|
||||
# activate different ways to authenticate
|
||||
# https://symfony.com/doc/current/security.html#the-firewall
|
||||
|
||||
# https://symfony.com/doc/current/security/impersonating_user.html
|
||||
# switch_user: true
|
||||
|
||||
# Easy way to control access for large sections of your site
|
||||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
# - { path: ^/admin, roles: ROLE_ADMIN }
|
||||
# - { path: ^/profile, roles: ROLE_USER }
|
||||
|
||||
when@test:
|
||||
security:
|
||||
password_hashers:
|
||||
# By default, password hashers are resource intensive and take time. This is
|
||||
# important to generate secure password hashes. In tests however, secure hashes
|
||||
# are not important, waste resources and increase test times. The following
|
||||
# reduces the work factor to the lowest possible values.
|
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
|
||||
algorithm: auto
|
||||
cost: 4 # Lowest possible value for bcrypt
|
||||
time_cost: 3 # Lowest possible value for argon
|
||||
memory_cost: 10 # Lowest possible value for argon
|
3
config/routes/security.yaml
Normal file
3
config/routes/security.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
_security_logout:
|
||||
resource: security.route_loader.logout
|
||||
type: service
|
@ -3,32 +3,86 @@
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Printer;
|
||||
use App\Enum\ErrorMessages;
|
||||
use App\Repository\PrinterRepository;
|
||||
use App\Service\PrinterService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class PrinterCrudController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly PrinterRepository $printerRepository)
|
||||
public function __construct(
|
||||
private readonly PrinterRepository $printerRepository,
|
||||
private readonly PrinterService $printerService,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/printer', name: 'printer')]
|
||||
public function printer(): JsonResponse
|
||||
#[Route('/printer', name: 'printer', methods: ['GET'])]
|
||||
public function getAllPrinters(): JsonResponse
|
||||
{
|
||||
return $this->json($this->printerRepository->findAll());
|
||||
}
|
||||
|
||||
#[Route('/printer/{id}', name: 'app_printer', methods: ["GET"])]
|
||||
public function index(?Printer $printer): JsonResponse
|
||||
#[Route('/printer/{id}', name: 'get_printer', methods: ['GET'])]
|
||||
public function getPrinter(?Printer $printer): JsonResponse
|
||||
{
|
||||
if (!$printer) {
|
||||
return $this->json([
|
||||
'message' => 'printer does not exist',
|
||||
'message' => ErrorMessages::DOESNT_EXIST->value,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->json($printer);
|
||||
}
|
||||
|
||||
#[Route('/printer/{id}', name: 'delete_printer', methods: ['DELETE'])]
|
||||
public function deletePrinter(?Printer $printer): JsonResponse
|
||||
{
|
||||
if (!$printer) {
|
||||
return $this->json([
|
||||
'message' => ErrorMessages::DOESNT_EXIST->value,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->printerService->deletePrinter($printer);
|
||||
|
||||
return $this->json([
|
||||
'message' => 'printer was deleted',
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/printer', name: 'create_printer', methods: ['POST'])]
|
||||
public function createPrinter(Request $request): JsonResponse
|
||||
{
|
||||
$jsonContent = $request->getContent();
|
||||
if (!$this->printerService->validateJson($jsonContent)) {
|
||||
return $this->json([
|
||||
'message' => ErrorMessages::DATA_INCOMPLETE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
$printer = $this->printerService->createPrinter($jsonContent);
|
||||
|
||||
return $this->json($printer);
|
||||
}
|
||||
|
||||
#[Route('/printer/{id}', name: 'edit_printer', methods: ['PUT'])]
|
||||
public function editPrinter(?Printer $printer, Request $request): JsonResponse
|
||||
{
|
||||
if (!$printer) {
|
||||
return $this->json([
|
||||
'message' => ErrorMessages::DOESNT_EXIST->value,
|
||||
]);
|
||||
}
|
||||
if (!$this->printerService->validateJson($request->getContent())) {
|
||||
return $this->json([
|
||||
'message' => ErrorMessages::DATA_INCOMPLETE,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
9
src/Enum/ErrorMessages.php
Normal file
9
src/Enum/ErrorMessages.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enum;
|
||||
|
||||
enum ErrorMessages: string
|
||||
{
|
||||
case DATA_INCOMPLETE = 'The provided data is incomplete';
|
||||
case DOESNT_EXIST = 'printer does not exist';
|
||||
}
|
44
src/Security/PrinterAuthenticator.php
Normal file
44
src/Security/PrinterAuthenticator.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
|
||||
class PrinterAuthenticator extends AbstractAuthenticator
|
||||
{
|
||||
public function supports(Request $request): ?bool
|
||||
{
|
||||
// TODO: Implement supports() method.
|
||||
}
|
||||
|
||||
public function authenticate(Request $request): Passport
|
||||
{
|
||||
// TODO: Implement authenticate() method.
|
||||
}
|
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
|
||||
{
|
||||
// TODO: Implement onAuthenticationSuccess() method.
|
||||
}
|
||||
|
||||
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
|
||||
{
|
||||
// TODO: Implement onAuthenticationFailure() method.
|
||||
}
|
||||
|
||||
// public function start(Request $request, AuthenticationException $authException = null): Response
|
||||
// {
|
||||
// /*
|
||||
// * If you would like this class to control what happens when an anonymous user accesses a
|
||||
// * protected page (e.g. redirect to /login), uncomment this method and make this class
|
||||
// * implement Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface.
|
||||
// *
|
||||
// * For more details, see https://symfony.com/doc/current/security/experimental_authenticators.html#configuring-the-authentication-entry-point
|
||||
// */
|
||||
// }
|
||||
}
|
@ -1,20 +1,35 @@
|
||||
<?php
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\Printer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
class PrinterService
|
||||
{
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager)
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly SerializerInterface $serializer)
|
||||
{
|
||||
}
|
||||
|
||||
public function deletePrinter(Printer $printer)
|
||||
public function deletePrinter(Printer $printer): void
|
||||
{
|
||||
$this->entityManager->remove($printer);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
public function createPrinter(string $jsonString): Printer
|
||||
{
|
||||
$printer = $this->serializer->deserialize($jsonString, Printer::class, 'json');
|
||||
$this->entityManager->persist($printer);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $printer;
|
||||
}
|
||||
|
||||
public function validateJson(string $jsonString): bool
|
||||
{
|
||||
$array = json_decode($jsonString, true);
|
||||
return isset($array['name'], $array['price'], $array['max_speed'], $array['build_volume']);
|
||||
}
|
||||
}
|
13
symfony.lock
13
symfony.lock
@ -104,5 +104,18 @@
|
||||
"./config/packages/routing.yaml",
|
||||
"./config/routes.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/security-bundle": {
|
||||
"version": "7.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "6.4",
|
||||
"ref": "2ae08430db28c8eb4476605894296c82a642028f"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/security.yaml",
|
||||
"config/routes/security.yaml"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -2,17 +2,82 @@
|
||||
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\Entity\Printer;
|
||||
use App\Service\PrinterService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PrinterService;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bundle\MakerBundle\Docker\DockerDatabaseServices;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use function PHPUnit\Framework\once;
|
||||
|
||||
class PrinterServiceTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function EditPrinterShouldEditPrinter(){
|
||||
$printerService = new PrinterService();
|
||||
private readonly EntityManagerInterface&MockObject $entityManager;
|
||||
private readonly SerializerInterface&MockObject $serializer;
|
||||
private readonly PrinterService $printerService;
|
||||
|
||||
$printerService->editPrinter(Printer $printer, )
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->entityManager = self::createMock(EntityManagerInterface::class);
|
||||
$this->serializer = self::createMock(SerializerInterface::class);
|
||||
$this->printerService = new PrinterService($this->entityManager, $this->serializer);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function removeShouldInvokeRemoveOnPrinterAndFlush()
|
||||
{
|
||||
$printer = new Printer();
|
||||
|
||||
$this->entityManager->expects(self::once())->method('remove')->with($printer);
|
||||
$this->entityManager->expects(self::once())->method('flush');
|
||||
|
||||
$this->printerService->deletePrinter($printer);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function validateJsonShouldReturnFalseOnInputIncompleteJson()
|
||||
{
|
||||
$invalidJson = '{
|
||||
"name": "Bambu A1"
|
||||
}';
|
||||
|
||||
self::assertFalse($this->printerService->validateJson($invalidJson));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function validateJsonShouldReturnTrueOnInputCompleteJson()
|
||||
{
|
||||
$validJson = '{
|
||||
"name": "Bambu A1 Mini",
|
||||
"price": 10.50,
|
||||
"build_volume": "180x180x180",
|
||||
"max_speed": 1000
|
||||
}';
|
||||
|
||||
self::assertTrue($this->printerService->validateJson($validJson));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createPrinterShouldInvokePersistAndFlushOnCreatedPrinterObject(){
|
||||
$json = '{
|
||||
"name": "Bambu A1 Mini",
|
||||
"price": 10.50,
|
||||
"build_volume": "180x180x180",
|
||||
"max_speed": 1000
|
||||
}';
|
||||
|
||||
$printer = new Printer();
|
||||
$printer
|
||||
->setName('Bambu A1 Mini')
|
||||
->setPrice(10.50)
|
||||
->setBuildVolume('180x180x180')
|
||||
->setMaxSpeed(1000);
|
||||
|
||||
$this->serializer->expects(self::once())->method('deserialize')->with($json)->willReturn($printer);
|
||||
$this->entityManager->expects(self::once())->method('persist')->with($printer);
|
||||
$this->entityManager->expects(self::once())->method('flush');
|
||||
|
||||
$this->printerService->createPrinter($json);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user