added authenticator so authentication is done via HTTP Header

develop
hecht 6 years ago
parent 0c861870c8
commit d13a6c4a6e

@ -7,14 +7,18 @@ App\Entity\User:
type: integer type: integer
generator: { strategy: AUTO } generator: { strategy: AUTO }
fields: fields:
name: username:
type: string type: string
length: 32 length: 32
unique: true unique: true
password: password: # the hashed password
type: string type: string
length: 64 apiToken:
type: string
unique: true
nullable: true
roles:
type: json
oneToMany: oneToMany:
heroes: heroes:
targetEntity: Hero targetEntity: Hero

@ -1,4 +1,8 @@
security: security:
encoders:
App\Entity\User:
algorithm: argon2i
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers: providers:
in_memory: { memory: ~ } in_memory: { memory: ~ }
@ -6,6 +10,12 @@ security:
dev: dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/ pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false security: false
api:
pattern: ^/api/
guard:
authenticators:
- App\Security\TokenAuthenticator
stateless: true
main: main:
anonymous: true anonymous: true

@ -2,7 +2,7 @@ App\Entity\User:
attributes: attributes:
id: id:
groups: ['Default'] groups: ['Default']
name: username:
groups: ['Default'] groups: ['Default']
user: user:
groups: ['HeroWithUser'] groups: ['HeroWithUser']

@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Response;
*/ */
class HeroController extends FOSRestController class HeroController extends FOSRestController
{ {
protected $HeroRepository; protected $heroRepository;
public function __construct(HeroRepository $heroRepository) public function __construct(HeroRepository $heroRepository)
{ {

@ -1,31 +1,47 @@
<?php <?php
namespace App\DataFixtures; namespace App\DataFixtures;
use App\Entity\User; use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager; use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserFixtures extends Fixture class UserFixtures extends Fixture
{ {
public const ADMIN_USER_REFERENCE = 'admin-user'; public const ADMIN_USER_REFERENCE = 'admin-user';
public const DUMMY_USER_REFERENCE = 'dummy-user';
public const DUDE_USER_REFERENCE = 'dummy-user';
private $passwordEncoder;
public function __construct(UserPasswordEncoderInterface $passwordEncoder)
{
$this->passwordEncoder = $passwordEncoder;
}
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
$userAdmin = new User(); $userAdmin = new User();
$userAdmin->setName('admin'); $userAdmin->setUsername('admin');
$userAdmin->setPassword('123456789'); $this->setPassword($userAdmin, '123456789');
$userAdmin->setApiToken('ItsHammerTime!');
$manager->persist($userAdmin); $manager->persist($userAdmin);
$userDummy = new User(); $userDude = new User();
$userDummy->setName('dummy'); $userDude->setUsername('dude');
$userDummy->setPassword('1234'); $this->setPassword($userDude, '1234');
$manager->persist($userDummy); $userDude->setApiToken('ItsDuderzeit!');
$manager->persist($userDude);
$manager->flush(); $manager->flush();
$this->addReference(self::ADMIN_USER_REFERENCE, $userAdmin); $this->addReference(self::ADMIN_USER_REFERENCE, $userAdmin);
$this->addReference(self::DUMMY_USER_REFERENCE, $userDummy); $this->addReference(self::DUDE_USER_REFERENCE, $userDude);
}
private function setPassword(User& $user, string $plainPassword): void
{
$user->setPassword($this->passwordEncoder->encodePassword($user, $plainPassword));
} }
} }

@ -4,15 +4,20 @@ namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Symfony\Component\Security\Core\User\UserInterface;
class User class User implements UserInterface
{ {
private $id; private $id;
private $name; private $username;
private $password; private $password;
private $apiToken;
private $roles = [];
private $heroes; private $heroes;
public function __construct() public function __construct()
@ -25,14 +30,17 @@ class User
return $this->id; return $this->id;
} }
public function getName(): ?string /**
* @see UserInterface
*/
public function getUsername(): ?string
{ {
return $this->name; return $this->username;
} }
public function setName(string $name): self public function setUsername(string $username): self
{ {
$this->name = $name; $this->username = $username;
return $this; return $this;
} }
@ -49,6 +57,30 @@ class User
return $this; return $this;
} }
public function getApiToken(): ?string
{
return $this->apiToken;
}
public function setApiToken(?string $apiToken): self
{
$this->apiToken = $apiToken;
return $this;
}
public function getRoles(): ?array
{
return $this->roles;
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/** /**
* @return Collection|Hero[] * @return Collection|Hero[]
*/ */
@ -79,4 +111,24 @@ class User
return $this; return $this;
} }
/**
* @see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yml
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
$this->apiToken = null;
}
} }

@ -0,0 +1,98 @@
<?php
namespace App\Security;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class TokenAuthenticator extends AbstractGuardAuthenticator
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* Called on every request to decide if this authenticator should be
* used for the request. Returning false will cause this authenticator
* to be skipped.
*/
public function supports(Request $request)
{
return $request->headers->has('X-AUTH-TOKEN');
}
/**
* Called on every request. Return whatever credentials you want to
* be passed to getUser() as $credentials.
*/
public function getCredentials(Request $request)
{
return array(
'token' => $request->headers->get('X-AUTH-TOKEN'),
);
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$apiToken = $credentials['token'];
if (null === $apiToken) {
return;
}
// if a User object, checkCredentials() is called
return $this->em->getRepository(User::class)
->findOneBy(['apiToken' => $apiToken]);
}
public function checkCredentials($credentials, UserInterface $user)
{
// check credentials - e.g. make sure the password is valid
// no credential check is needed in this case
// return true to cause authentication success
return true;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
// or to translate this message
// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
// This should translated by FOSRestBundle!
throw new AccessDeniedHttpException($message);
}
/**
* Called when authentication is needed, but it's not sent
*/
public function start(Request $request, AuthenticationException $authException = null)
{
throw new UnauthorizedHttpException('', 'Authentication Required');
}
public function supportsRememberMe()
{
return false;
}
}

@ -0,0 +1,12 @@
<?php
namespace Tests\App;
use Symfony\Component\BrowserKit\Client;
use Symfony\Component\DomCrawler\Crawler;
class ClientRequestBuilder
{
}
Loading…
Cancel
Save