This commit is contained in:
Ivan Perelomov 2022-01-08 19:15:26 +07:00
parent a00741861a
commit 6ed74c2284
39 changed files with 4235 additions and 4 deletions

8
.env
View File

@ -17,3 +17,11 @@
APP_ENV=dev APP_ENV=dev
APP_SECRET=23cbc3b31c910f18a9822666f2bdb3ef APP_SECRET=23cbc3b31c910f18a9822666f2bdb3ef
###< symfony/framework-bundle ### ###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
DATABASE_URL="mysql://app:password@mysql:3306/app"
###< doctrine/doctrine-bundle ###

2
.gitignore vendored
View File

@ -8,3 +8,5 @@
/var/ /var/
/vendor/ /vendor/
###< symfony/framework-bundle ### ###< symfony/framework-bundle ###
/.idea/

View File

@ -7,14 +7,23 @@
"php": ">=7.2.5", "php": ">=7.2.5",
"ext-ctype": "*", "ext-ctype": "*",
"ext-iconv": "*", "ext-iconv": "*",
"composer/package-versions-deprecated": "1.11.99.4",
"doctrine/doctrine-bundle": "^2.5",
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.10",
"symfony/console": "5.4.*", "symfony/console": "5.4.*",
"symfony/dotenv": "5.4.*", "symfony/dotenv": "5.4.*",
"symfony/flex": "^1.17|^2", "symfony/flex": "^1.17|^2",
"symfony/framework-bundle": "5.4.*", "symfony/framework-bundle": "5.4.*",
"symfony/maker-bundle": "^1.36",
"symfony/proxy-manager-bridge": "5.4.*",
"symfony/runtime": "5.4.*", "symfony/runtime": "5.4.*",
"symfony/yaml": "5.4.*" "symfony/security-bundle": "5.4.*",
}, "symfony/twig-bundle": "5.4.*",
"require-dev": { "symfony/web-profiler-bundle": "5.4.*",
"symfony/yaml": "5.4.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {

3160
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,4 +2,11 @@
return [ return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
]; ];

View File

@ -0,0 +1,6 @@
web_profiler:
toolbar: true
intercept_redirects: false
framework:
profiler: { only_exceptions: false }

View File

@ -0,0 +1,17 @@
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '13'
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App

View File

@ -0,0 +1,6 @@
doctrine_migrations:
migrations_paths:
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
'DoctrineMigrations': '%kernel.project_dir%/migrations'
enable_profiler: '%kernel.debug%'

View File

@ -0,0 +1,17 @@
doctrine:
orm:
auto_generate_proxy_classes: false
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system

View File

@ -0,0 +1,49 @@
security:
enable_authenticator_manager: true
# 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:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
custom_authenticator: App\Security\UserAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
# 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: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, 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

View File

@ -0,0 +1,4 @@
doctrine:
dbal:
# "TEST_TOKEN" is typically set by ParaTest
dbname_suffix: '_test%env(default::TEST_TOKEN)%'

View File

@ -0,0 +1,6 @@
web_profiler:
toolbar: false
intercept_redirects: false
framework:
profiler: { collect: false }

View File

@ -0,0 +1,6 @@
twig:
default_path: '%kernel.project_dir%/templates'
when@test:
twig:
strict_variables: true

View File

@ -0,0 +1,7 @@
controllers:
resource: ../../src/Controller/
type: annotation
kernel:
resource: ../../src/Kernel.php
type: annotation

View File

@ -0,0 +1,7 @@
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
prefix: /_profiler

43
docker-compose.yml Normal file
View File

@ -0,0 +1,43 @@
version: '3'
services:
mysql:
image: mysql:8.0.22
container_name: mysql
restart: always
volumes:
- ./var/mysql:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: app
MYSQL_USER: app
MYSQL_PASSWORD: password
ports:
- "3306:3306"
nginx:
image: nginx:stable
container_name: nginx
restart: always
volumes:
- ./:/app
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
ports:
- "80:80"
links:
- php-fpm
php-fpm:
build: docker/php-fpm
restart: always
container_name: php-fpm
volumes:
- ./:/app
- ./docker/php-fpm/php.ini:/usr/local/etc/php/php.ini
links:
- mysql
volumes:
###> doctrine/doctrine-bundle ###
db-data:
###< doctrine/doctrine-bundle ###

40
docker/nginx/nginx.conf Normal file
View File

@ -0,0 +1,40 @@
worker_processes 1;
events {
worker_connections 2048;
}
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 200M;
sendfile on;
keepalive_timeout 65;
server {
listen 80 default_server;
chunked_transfer_encoding off;
server_name platform.local;
root /app/public;
index index.php;
try_files $uri $uri/ /index.php?$query_string;
default_type text/html;
access_log /app/var/log/nginx-access.log;
error_log /app/var/log/nginx-error.log error;
location ~ /upload/\. {
deny all;
}
location ~* /upload/.*$ {
add_header Content-Disposition "attachment";
add_header Content-Type application/octet-stream;
}
location ~* \.php$ {
fastcgi_pass php-fpm:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
}
}
}

View File

@ -0,0 +1,4 @@
FROM php:8.1.0-fpm
WORKDIR /app
RUN docker-php-ext-install pdo pdo_mysql

3
docker/php-fpm/php.ini Normal file
View File

@ -0,0 +1,3 @@
date.timezone=Europe/Moscow
upload_max_filesize = 200M
post_max_size = 200M

0
migrations/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220108091848 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE `user` (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(180) NOT NULL, score INT NOT NULL, log TINYINT(1) NOT NULL, roles JSON NOT NULL, UNIQUE INDEX UNIQ_8D93D649F85E0677 (username), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE user_log (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, score INT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE `user`');
$this->addSql('DROP TABLE user_log');
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220108121138 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE INDEX IDX_8D93D649F85E0677 ON user (username)');
$this->addSql('CREATE INDEX IDX_8D93D64932993751 ON user (score)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX IDX_8D93D649F85E0677 ON `user`');
$this->addSql('DROP INDEX IDX_8D93D64932993751 ON `user`');
}
}

79
run Executable file
View File

@ -0,0 +1,79 @@
#!/bin/bash
case "$1" in
shortlist)
echo consumer-restart consumer-restart-all
exit 1
;;
up)
docker-compose up -d --force-recreate --remove-orphans $2
;;
down)
docker-compose down $2
;;
stop)
docker-compose stop $2
;;
restart)
docker-compose restart $2
;;
update)
git pull
composer install
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:migrate --no-interaction
docker-compose exec php-fpm php /app/bin/console c:c
;;
migrate-run)
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:migrate --no-interaction
;;
migrate-redo)
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:execute $2 --down --no-interaction
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:execute $2 --up --no-interaction
;;
migrate-create)
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:generate
;;
build)
docker-compose build
;;
cc)
docker-compose exec php-fpm php /app/bin/console c:c
;;
command)
docker-compose exec php-fpm php /app/bin/console $2 ${@:3}
;;
composer-install)
docker-compose exec cron composer install
;;
composer-update)
docker-compose exec cron composer update
;;
dump-sql-schema)
docker-compose exec php-fpm php /app/bin/console doctrine:schema:create --dump-sql > config/base.sql
;;
*)
echo "Usage: $0 {command}
consumer-restart {consumer} - restart consumer
consumer-restart-all - restart all consumer
consumer-reread - reread supervisor config file (docker/supervisor/supervisord.conf -> /etc/supervisor/conf.d/supervisord.conf)
up - up docker
down - down docker
stop {name] - stop single docker container
restart {name} - restart all or single docker container
update - update project (git puul, composer install, run migrate, clear cache)
migrate-run - run migrate
migrate-redo {id} - redo single migrate
migrate-create - Generate a blank migration class
build - build project
cc - clear cache
command {name} - execute command
composer-install - composer install
composer-update - composer update
dump-sql-schema - create sql schema
" >&2
exit 1
;;
esac
exit 0

View File

@ -0,0 +1,47 @@
<?php
namespace App\Command;
use App\Entity\User;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:create-user',
description: 'Add a short description for your command',
)]
class CreateUserCommand extends Command
{
private EntityManager $entityManager;
/**
* @param EntityManager $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
for ($i=0;$i<100000;$i++) {
$user = new User();
$user->setUsername('user' . random_int(10000000000, 99999999999))
->setScore(random_int(0, 1000))
->setLog(false);
$this->entityManager->persist($user);
}
$this->entityManager->flush();
return Command::SUCCESS;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Command;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:update-user-score',
description: 'Add a short description for your command',
)]
class UpdateUserScoreCommand extends Command
{
private EntityManagerInterface $entityManager;
/**
* @param EntityManagerInterface $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$this->entityManager->getConnection()->executeQuery('
UPDATE `user` SET score = RAND() * 1000
');
return Command::SUCCESS;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Controller;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class MainController extends AbstractController
{
#[Route('/', name: 'main')]
public function index(): Response
{
return $this->render('main.html.twig');
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
/**
* @Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
// if ($this->getUser()) {
// return $this->redirectToRoute('target_path');
// }
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* @Route("/logout", name="app_logout")
*/
public function logout(): void
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}

0
src/Entity/.gitignore vendored Normal file
View File

134
src/Entity/User.php Normal file
View File

@ -0,0 +1,134 @@
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: '`user`')]
#[ORM\Index(columns: ['username'])]
#[ORM\Index(columns: ['score'])]
class User implements UserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int $id;
#[ORM\Column(type: 'string', length: 180, unique: true)]
private string $username;
#[ORM\Column(type: 'integer')]
private int $score;
#[ORM\Column(type: 'boolean')]
private bool $log;
#[ORM\Column(type: 'json')]
private array $roles = [];
public function getId(): ?int
{
return $this->id;
}
/**
* @deprecated since Symfony 5.3, use getUserIdentifier instead
*/
public function getUsername(): string
{
return (string) $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getScore(): ?int
{
return $this->score;
}
public function setScore(int $score): self
{
$this->score = $score;
return $this;
}
public function getLog(): ?bool
{
return $this->log;
}
public function setLog(bool $log): self
{
$this->log = $log;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->username;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* This method can be removed in Symfony 6.0 - is not needed for apps that do not check user passwords.
*
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): ?string
{
return null;
}
/**
* This method can be removed in Symfony 6.0 - is not needed for apps that do not check user passwords.
*
* @see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}

65
src/Entity/UserLog.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace App\Entity;
use App\Repository\UserLogRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: UserLogRepository::class)]
class UserLog
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int $id;
#[ORM\Column(type: 'integer')]
private int $user_id;
#[ORM\Column(type: 'integer')]
private int $score;
#[ORM\Column(type: 'datetime_immutable')]
private \DateTimeImmutable $created_at;
public function getId(): ?int
{
return $this->id;
}
public function getUserId(): ?int
{
return $this->user_id;
}
public function setUserId(int $user_id): self
{
$this->user_id = $user_id;
return $this;
}
public function getScore(): ?int
{
return $this->score;
}
public function setScore(int $score): self
{
$this->score = $score;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->created_at;
}
public function setCreatedAt(\DateTimeImmutable $created_at): self
{
$this->created_at = $created_at;
return $this;
}
}

0
src/Repository/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,21 @@
<?php
namespace App\Repository;
use App\Entity\UserLog;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method UserLog|null find($id, $lockMode = null, $lockVersion = null)
* @method UserLog|null findOneBy(array $criteria, array $orderBy = null)
* @method UserLog[] findAll()
* @method UserLog[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserLogRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, UserLog::class);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Security;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CredentialsInterface;
class EmptyPassword implements CredentialsInterface
{
public function isResolved(): bool
{
return true;
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class UserAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function authenticate(Request $request): Passport
{
$username = $request->request->get('username', '');
$request->getSession()->set(Security::LAST_USERNAME, $username);
return new Passport(
new UserBadge($username),
new EmptyPassword(),
[
new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return new RedirectResponse($this->urlGenerator->generate('main'));
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}

View File

@ -1,4 +1,96 @@
{ {
"composer/package-versions-deprecated": {
"version": "1.11.99.4"
},
"doctrine/annotations": {
"version": "1.13",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.0",
"ref": "a2759dd6123694c8d901d0ec80006e044c2e6457"
},
"files": [
"config/routes/annotations.yaml"
]
},
"doctrine/cache": {
"version": "2.1.1"
},
"doctrine/collections": {
"version": "1.6.8"
},
"doctrine/common": {
"version": "3.2.1"
},
"doctrine/dbal": {
"version": "3.2.1"
},
"doctrine/deprecations": {
"version": "v0.5.3"
},
"doctrine/doctrine-bundle": {
"version": "2.5",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "2.4",
"ref": "f98f1affe028f8153a459d15f220ada3826b5aa2"
},
"files": [
"config/packages/doctrine.yaml",
"config/packages/prod/doctrine.yaml",
"config/packages/test/doctrine.yaml",
"src/Entity/.gitignore",
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-migrations-bundle": {
"version": "3.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "3.1",
"ref": "ee609429c9ee23e22d6fa5728211768f51ed2818"
},
"files": [
"config/packages/doctrine_migrations.yaml",
"migrations/.gitignore"
]
},
"doctrine/event-manager": {
"version": "1.1.1"
},
"doctrine/inflector": {
"version": "2.0.4"
},
"doctrine/instantiator": {
"version": "1.4.0"
},
"doctrine/lexer": {
"version": "1.2.1"
},
"doctrine/migrations": {
"version": "3.3.2"
},
"doctrine/orm": {
"version": "2.10.4"
},
"doctrine/persistence": {
"version": "2.2.3"
},
"doctrine/sql-formatter": {
"version": "1.1.2"
},
"friendsofphp/proxy-manager-lts": {
"version": "v1.0.5"
},
"laminas/laminas-code": {
"version": "4.5.1"
},
"nikic/php-parser": {
"version": "v4.13.2"
},
"psr/cache": { "psr/cache": {
"version": "2.0.0" "version": "2.0.0"
}, },
@ -38,6 +130,9 @@
"symfony/deprecation-contracts": { "symfony/deprecation-contracts": {
"version": "v3.0.0" "version": "v3.0.0"
}, },
"symfony/doctrine-bridge": {
"version": "v5.4.2"
},
"symfony/dotenv": { "symfony/dotenv": {
"version": "v5.4.2" "version": "v5.4.2"
}, },
@ -93,6 +188,18 @@
"symfony/http-kernel": { "symfony/http-kernel": {
"version": "v5.4.2" "version": "v5.4.2"
}, },
"symfony/maker-bundle": {
"version": "1.36",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.0",
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/password-hasher": {
"version": "v5.4.2"
},
"symfony/polyfill-intl-grapheme": { "symfony/polyfill-intl-grapheme": {
"version": "v1.24.0" "version": "v1.24.0"
}, },
@ -111,6 +218,15 @@
"symfony/polyfill-php81": { "symfony/polyfill-php81": {
"version": "v1.24.0" "version": "v1.24.0"
}, },
"symfony/property-access": {
"version": "v5.4.2"
},
"symfony/property-info": {
"version": "v5.4.2"
},
"symfony/proxy-manager-bridge": {
"version": "v5.4.2"
},
"symfony/routing": { "symfony/routing": {
"version": "5.4", "version": "5.4",
"recipe": { "recipe": {
@ -127,19 +243,85 @@
"symfony/runtime": { "symfony/runtime": {
"version": "v5.4.1" "version": "v5.4.1"
}, },
"symfony/security-bundle": {
"version": "5.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.3",
"ref": "09b5e809bd7a992061febd05b797c64a2d93b5cd"
},
"files": [
"config/packages/security.yaml"
]
},
"symfony/security-core": {
"version": "v5.4.2"
},
"symfony/security-csrf": {
"version": "v5.4.0"
},
"symfony/security-guard": {
"version": "v5.4.0"
},
"symfony/security-http": {
"version": "v5.4.2"
},
"symfony/service-contracts": { "symfony/service-contracts": {
"version": "v2.4.1" "version": "v2.4.1"
}, },
"symfony/stopwatch": {
"version": "v5.4.0"
},
"symfony/string": { "symfony/string": {
"version": "v5.4.2" "version": "v5.4.2"
}, },
"symfony/translation-contracts": {
"version": "v2.5.0"
},
"symfony/twig-bridge": {
"version": "v5.4.0"
},
"symfony/twig-bundle": {
"version": "5.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.4",
"ref": "bffbb8f1a849736e64006735afae730cb428b6ff"
},
"files": [
"config/packages/twig.yaml",
"templates/base.html.twig"
]
},
"symfony/var-dumper": { "symfony/var-dumper": {
"version": "v5.4.2" "version": "v5.4.2"
}, },
"symfony/var-exporter": { "symfony/var-exporter": {
"version": "v5.4.2" "version": "v5.4.2"
}, },
"symfony/web-profiler-bundle": {
"version": "5.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "3.3",
"ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6"
},
"files": [
"config/packages/dev/web_profiler.yaml",
"config/packages/test/web_profiler.yaml",
"config/routes/dev/web_profiler.yaml"
]
},
"symfony/yaml": { "symfony/yaml": {
"version": "v5.4.2" "version": "v5.4.2"
},
"twig/extra-bundle": {
"version": "v3.3.7"
},
"twig/twig": {
"version": "v3.3.7"
} }
} }

19
templates/base.html.twig Normal file
View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>

7
templates/main.html.twig Normal file
View File

@ -0,0 +1,7 @@
{% extends 'base.html.twig' %}
{% block title %}Main!{% endblock %}
{% block body %}
Main
{% endblock %}

View File

@ -0,0 +1,29 @@
{% extends 'base.html.twig' %}
{% block title %}Log in!{% endblock %}
{% block body %}
<form method="post">
{% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
{% if app.user %}
<div class="mb-3">
You are logged in as {{ app.user.username }}, <a href="{{ path('app_logout') }}">Logout</a>
</div>
{% endif %}
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<label for="inputUsername">Username</label>
<input type="text" value="{{ last_username }}" name="username" id="inputUsername" class="form-control" autocomplete="username" required autofocus>
<input type="hidden" name="_csrf_token"
value="{{ csrf_token('authenticate') }}"
>
<button class="btn btn-lg btn-primary" type="submit">
Sign in
</button>
</form>
{% endblock %}