Browse Source

ntroduction des users

master
vincent 1 month ago
parent
commit
86c80a83cf
14 changed files with 862 additions and 12 deletions
  1. +1
    -0
      composer.json
  2. +495
    -1
      composer.lock
  3. +1
    -0
      config/bundles.php
  4. +48
    -0
      config/packages/security.yaml
  5. +1
    -1
      config/packages/twig.yaml
  6. +3
    -0
      config/routes/security.yaml
  7. +33
    -0
      migrations/Version20240726120017.php
  8. +9
    -6
      src/Controller/HomeController.php
  9. +101
    -0
      src/Entity/User.php
  10. +43
    -0
      src/Repository/UserRepository.php
  11. +83
    -0
      src/Security/OpenStreetMapAuthenticator.php
  12. +13
    -0
      symfony.lock
  13. +23
    -1
      templates/_header.html.twig
  14. +8
    -3
      templates/home/index.html.twig

+ 1
- 0
composer.json View File

@ -26,6 +26,7 @@
"symfony/form": "7.1.*",
"symfony/framework-bundle": "7.1.*",
"symfony/runtime": "7.1.*",
"symfony/security-bundle": "7.1.*",
"symfony/stimulus-bundle": "^2.18",
"symfony/twig-bundle": "7.1.*",
"symfony/validator": "7.1.*",


+ 495
- 1
composer.lock View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ab07552debf0ed36325858962a6ddb7a",
"content-hash": "d673dbc41a6c7573a7676813f6beb0c1",
"packages": [
{
"name": "behat/transliterator",
@ -3708,6 +3708,80 @@
"time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/clock",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/clock.git",
"reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7",
"reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7",
"shasum": ""
},
"require": {
"php": ">=8.2",
"psr/clock": "^1.0",
"symfony/polyfill-php83": "^1.28"
},
"provide": {
"psr/clock-implementation": "1.0"
},
"type": "library",
"autoload": {
"files": [
"Resources/now.php"
],
"psr-4": {
"Symfony\\Component\\Clock\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Decouples applications from the system clock",
"homepage": "https://symfony.com",
"keywords": [
"clock",
"psr20",
"time"
],
"support": {
"source": "https://github.com/symfony/clock/tree/v7.1.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
},
{
"name": "symfony/config",
"version": "v7.1.1",
"source": {
@ -5391,6 +5465,78 @@
"time": "2024-05-31T14:57:53+00:00"
},
{
"name": "symfony/password-hasher",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/password-hasher.git",
"reference": "4ad96eb7cf9e2f8f133ada95f2b8021769061662"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/password-hasher/zipball/4ad96eb7cf9e2f8f133ada95f2b8021769061662",
"reference": "4ad96eb7cf9e2f8f133ada95f2b8021769061662",
"shasum": ""
},
"require": {
"php": ">=8.2"
},
"conflict": {
"symfony/security-core": "<6.4"
},
"require-dev": {
"symfony/console": "^6.4|^7.0",
"symfony/security-core": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\PasswordHasher\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Robin Chalas",
"email": "robin.chalas@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides password hashing utilities",
"homepage": "https://symfony.com",
"keywords": [
"hashing",
"password"
],
"support": {
"source": "https://github.com/symfony/password-hasher/tree/v7.1.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.30.0",
"source": {
@ -6110,6 +6256,354 @@
"time": "2024-05-31T14:55:39+00:00"
},
{
"name": "symfony/security-bundle",
"version": "v7.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-bundle.git",
"reference": "a6746372202512d2c75ba9bdbc36e15022a56c42"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/security-bundle/zipball/a6746372202512d2c75ba9bdbc36e15022a56c42",
"reference": "a6746372202512d2c75ba9bdbc36e15022a56c42",
"shasum": ""
},
"require": {
"composer-runtime-api": ">=2.1",
"ext-xml": "*",
"php": ">=8.2",
"symfony/clock": "^6.4|^7.0",
"symfony/config": "^6.4|^7.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/event-dispatcher": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/password-hasher": "^6.4|^7.0",
"symfony/security-core": "^6.4|^7.0",
"symfony/security-csrf": "^6.4|^7.0",
"symfony/security-http": "^7.1",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
"symfony/browser-kit": "<6.4",
"symfony/console": "<6.4",
"symfony/framework-bundle": "<6.4",
"symfony/http-client": "<6.4",
"symfony/ldap": "<6.4",
"symfony/serializer": "<6.4",
"symfony/twig-bundle": "<6.4",
"symfony/validator": "<6.4"
},
"require-dev": {
"symfony/asset": "^6.4|^7.0",
"symfony/browser-kit": "^6.4|^7.0",
"symfony/console": "^6.4|^7.0",
"symfony/css-selector": "^6.4|^7.0",
"symfony/dom-crawler": "^6.4|^7.0",
"symfony/expression-language": "^6.4|^7.0",
"symfony/form": "^6.4|^7.0",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0",
"symfony/ldap": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/rate-limiter": "^6.4|^7.0",
"symfony/serializer": "^6.4|^7.0",
"symfony/translation": "^6.4|^7.0",
"symfony/twig-bridge": "^6.4|^7.0",
"symfony/twig-bundle": "^6.4|^7.0",
"symfony/validator": "^6.4|^7.0",
"symfony/yaml": "^6.4|^7.0",
"twig/twig": "^3.0.4",
"web-token/jwt-library": "^3.3.2"
},
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Symfony\\Bundle\\SecurityBundle\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides a tight integration of the Security component into the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/security-bundle/tree/v7.1.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-06-28T10:35:32+00:00"
},
{
"name": "symfony/security-core",
"version": "v7.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-core.git",
"reference": "d615960211a11913e70f8576e5c38cd05d90ec3f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/security-core/zipball/d615960211a11913e70f8576e5c38cd05d90ec3f",
"reference": "d615960211a11913e70f8576e5c38cd05d90ec3f",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/event-dispatcher-contracts": "^2.5|^3",
"symfony/password-hasher": "^6.4|^7.0",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
"symfony/dependency-injection": "<6.4",
"symfony/event-dispatcher": "<6.4",
"symfony/http-foundation": "<6.4",
"symfony/ldap": "<6.4",
"symfony/translation": "<6.4.3|>=7.0,<7.0.3",
"symfony/validator": "<6.4"
},
"require-dev": {
"psr/cache": "^1.0|^2.0|^3.0",
"psr/container": "^1.1|^2.0",
"psr/log": "^1|^2|^3",
"symfony/cache": "^6.4|^7.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/event-dispatcher": "^6.4|^7.0",
"symfony/expression-language": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/ldap": "^6.4|^7.0",
"symfony/string": "^6.4|^7.0",
"symfony/translation": "^6.4.3|^7.0.3",
"symfony/validator": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Security\\Core\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Security Component - Core Library",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/security-core/tree/v7.1.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-06-28T08:00:31+00:00"
},
{
"name": "symfony/security-csrf",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-csrf.git",
"reference": "27cd1bce9d7f3457a152a6ca9790712d6954dd21"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/security-csrf/zipball/27cd1bce9d7f3457a152a6ca9790712d6954dd21",
"reference": "27cd1bce9d7f3457a152a6ca9790712d6954dd21",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/security-core": "^6.4|^7.0"
},
"conflict": {
"symfony/http-foundation": "<6.4"
},
"require-dev": {
"symfony/http-foundation": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Security\\Csrf\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Security Component - CSRF Library",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/security-csrf/tree/v7.1.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
},
{
"name": "symfony/security-http",
"version": "v7.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-http.git",
"reference": "e9dc3ef093dac3d4982fcd96002525aa7a1d6989"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/security-http/zipball/e9dc3ef093dac3d4982fcd96002525aa7a1d6989",
"reference": "e9dc3ef093dac3d4982fcd96002525aa7a1d6989",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/polyfill-mbstring": "~1.0",
"symfony/property-access": "^6.4|^7.0",
"symfony/security-core": "^6.4|^7.0",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
"symfony/clock": "<6.4",
"symfony/event-dispatcher": "<6.4",
"symfony/http-client-contracts": "<3.0",
"symfony/security-bundle": "<6.4",
"symfony/security-csrf": "<6.4"
},
"require-dev": {
"psr/log": "^1|^2|^3",
"symfony/cache": "^6.4|^7.0",
"symfony/clock": "^6.4|^7.0",
"symfony/expression-language": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0",
"symfony/http-client-contracts": "^3.0",
"symfony/rate-limiter": "^6.4|^7.0",
"symfony/routing": "^6.4|^7.0",
"symfony/security-csrf": "^6.4|^7.0",
"symfony/translation": "^6.4|^7.0",
"web-token/jwt-library": "^3.3.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Security\\Http\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Security Component - HTTP Integration",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/security-http/tree/v7.1.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-06-25T19:55:06+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v3.5.0",
"source": {


+ 1
- 0
config/bundles.php View File

@ -14,4 +14,5 @@ return [
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
];

+ 48
- 0
config/packages/security.yaml View File

@ -0,0 +1,48 @@
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:
# 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_authenticators:
- App\Security\OpenStreetMapAuthenticator
logout:
path: /osm/logout
target: app_home
# 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

+ 1
- 1
config/packages/twig.yaml View File

@ -5,7 +5,7 @@ twig:
title: Gestionnaire de tâches simple
menu:
header:
- { route: 'app_project', label: 'Projets' }
- { route: 'app_project', label: 'Projets', icon: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-folder" viewBox="0 0 16 16"><path d="M.54 3.87.5 3a2 2 0 0 1 2-2h3.672a2 2 0 0 1 1.414.586l.828.828A2 2 0 0 0 9.828 3h3.982a2 2 0 0 1 1.992 2.181l-.637 7A2 2 0 0 1 13.174 14H2.826a2 2 0 0 1-1.991-1.819l-.637-7a2 2 0 0 1 .342-1.31zM2.19 4a1 1 0 0 0-.996 1.09l.637 7a1 1 0 0 0 .995.91h10.348a1 1 0 0 0 .995-.91l.637-7A1 1 0 0 0 13.81 4zm4.69-1.707A1 1 0 0 0 6.172 2H2.5a1 1 0 0 0-1 .981l.006.139q.323-.119.684-.12h5.396z"/></svg>' }
taskLifecycleManager: '@App\Service\TaskLifecycleManager'
geoJsonManager: '@App\Service\GeoJsonManager'


+ 3
- 0
config/routes/security.yaml View File

@ -0,0 +1,3 @@
_security_logout:
resource: security.route_loader.logout
type: service

+ 33
- 0
migrations/Version20240726120017.php 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 Version20240726120017 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 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username VARCHAR(180) NOT NULL, roles CLOB NOT NULL --(DC2Type:json)
, osm_id INTEGER NOT NULL)');
$this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_USERNAME ON user (username)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE user');
}
}

+ 9
- 6
src/Controller/HomeController.php View File

@ -5,11 +5,11 @@ namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class HomeController extends AbstractController
{
#[Route('/', name: 'app_home')]
@ -36,16 +36,19 @@ class HomeController extends AbstractController
try {
$resourceOwner = $client->fetchUser();
dump(
$resourceOwner->getId(),
$resourceOwner->getDisplayName()
);
$this->addFlash('success', 'Authentification OSM réussie !');
} catch (IdentityProviderException $exception) {
$this->addFlash('success', 'Échec de l’authentification OSM !');
$this->addFlash('danger', 'Échec de l’authentification OSM !');
}
return $this->redirectToRoute('app_home');
}
#[Route('/osm/logout', name: 'app_osm_logout')]
public function osmLogout(Security $security): Response
{
return $security->logout();
}
}

+ 101
- 0
src/Entity/User.php View File

@ -0,0 +1,101 @@
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_USERNAME', fields: ['username'])]
class User implements UserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180)]
private ?string $username = null;
/**
* @var list<string> The user roles
*/
#[ORM\Column]
private array $roles = [];
#[ORM\Column]
private ?int $osmId = null;
public function getId(): ?int
{
return $this->id;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): static
{
$this->username = $username;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->username;
}
/**
* @see UserInterface
*
* @return list<string>
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
/**
* @param list<string> $roles
*/
public function setRoles(array $roles): static
{
$this->roles = $roles;
return $this;
}
/**
* @see UserInterface
*/
public function eraseCredentials(): void
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getOsmId(): ?int
{
return $this->osmId;
}
public function setOsmId(int $osmId): static
{
$this->osmId = $osmId;
return $this;
}
}

+ 43
- 0
src/Repository/UserRepository.php View File

@ -0,0 +1,43 @@
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<User>
*/
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
// /**
// * @return User[] Returns an array of User objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('u')
// ->andWhere('u.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('u.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?User
// {
// return $this->createQueryBuilder('u')
// ->andWhere('u.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

+ 83
- 0
src/Security/OpenStreetMapAuthenticator.php View File

@ -0,0 +1,83 @@
<?php
namespace App\Security;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class OpenStreetMapAuthenticator extends OAuth2Authenticator implements AuthenticationEntrypointInterface
{
private $clientRegistry;
private $entityManager;
private $router;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $entityManager, RouterInterface $router)
{
$this->clientRegistry = $clientRegistry;
$this->entityManager = $entityManager;
$this->router = $router;
}
public function supports(Request $request): ?bool
{
return $request->attributes->get('_route') === 'app_osm_callback';
}
public function authenticate(Request $request): Passport
{
$client = $this->clientRegistry->getClient('openstreetmap');
$accessToken = $this->fetchAccessToken($client);
return new SelfValidatingPassport(
new UserBadge($accessToken->getToken(), function() use ($accessToken, $client) {
$resourceOwner = $client->fetchUserFromToken($accessToken);
$existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['osmId' => $resourceOwner->getId()]);
if ($existingUser) {
return $existingUser;
}
$user = new User();
$user->setOsmId($resourceOwner->getId());
$user->setUsername($resourceOwner->getDisplayName());
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
})
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
return null;
}
public function start(Request $request, AuthenticationException $authException = null): Response
{
return new RedirectResponse(
'/',
Response::HTTP_TEMPORARY_REDIRECT
);
}
}

+ 13
- 0
symfony.lock View File

@ -157,6 +157,19 @@
"config/routes.yaml"
]
},
"symfony/security-bundle": {
"version": "7.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.4",
"ref": "2ae08430db28c8eb4476605894296c82a642028f"
},
"files": [
"config/packages/security.yaml",
"config/routes/security.yaml"
]
},
"symfony/stimulus-bundle": {
"version": "2.18",
"recipe": {


+ 23
- 1
templates/_header.html.twig View File

@ -9,9 +9,31 @@
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
{% for item in menu.header %}
<li class="nav-item">
<a {% if app.request.attributes.get('_route') == item.route %}class="nav-link active" aria-current="page"{% else %}class="nav-link"{% endif %} href="{{ path(item.route) }}">{{ item.label }}</a>
<a {% if app.request.attributes.get('_route') == item.route %}class="nav-link active" aria-current="page"{% else %}class="nav-link"{% endif %} href="{{ path(item.route) }}">{{ item.icon|raw }} {{ item.label }}</a>
</li>
{% endfor %}
{% if is_granted('IS_AUTHENTICATED_FULLY') %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person" viewBox="0 0 16 16">
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10s-3.516.68-4.168 1.332c-.678.678-.83 1.418-.832 1.664z"/>
</svg>
{{ app.user.username }}
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ path('app_osm_logout') }}">Déconnexion</a></li>
</ul>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ path('app_osm_request') }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person" viewBox="0 0 16 16">
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10s-3.516.68-4.168 1.332c-.678.678-.83 1.418-.832 1.664z"/>
</svg>
Connexion
</a>
</li>
{% endif %}
</ul>
</div>
</div>


+ 8
- 3
templates/home/index.html.twig View File

@ -1,9 +1,14 @@
{% extends 'base.html.twig' %}
{% block page_content %}
<div class="row">
<div class="col">
<a class="btn btn-primary" href="{{ path('app_osm_request') }}">S’authentifier avec OSM</a>
<div class="px-4 py-5 my-5 text-center">
<h1 class="display-5 fw-bold text-body-emphasis">Gestionnaire de tâches simple</h1>
<div class="col-lg-6 mx-auto">
<p class="lead mb-4">L’idée c’est d’avoir un petit outil collaboratif simple et facile à administrer/utiliser pour mapper des trucs de façon coordonnée à un endroit.</p>
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
<a class="btn btn-primary btn-lg px-4 gap-3" href="{{ path('app_project') }}">Voir les projets</a>
</div>
</div>
</div>
</div>
{% endblock %}

Loading…
Cancel
Save