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
generator: { strategy: AUTO }
fields:
name:
username:
type: string
length: 32
unique: true
password:
type: string
length: 64
password: # the hashed password
type: string
apiToken:
type: string
unique: true
nullable: true
roles:
type: json
oneToMany:
heroes:
targetEntity: Hero

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

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

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

@ -1,31 +1,47 @@
<?php
namespace App\DataFixtures;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserFixtures extends Fixture
{
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)
{
$userAdmin = new User();
$userAdmin->setName('admin');
$userAdmin->setPassword('123456789');
$userAdmin->setUsername('admin');
$this->setPassword($userAdmin, '123456789');
$userAdmin->setApiToken('ItsHammerTime!');
$manager->persist($userAdmin);
$userDummy = new User();
$userDummy->setName('dummy');
$userDummy->setPassword('1234');
$manager->persist($userDummy);
$userDude = new User();
$userDude->setUsername('dude');
$this->setPassword($userDude, '1234');
$userDude->setApiToken('ItsDuderzeit!');
$manager->persist($userDude);
$manager->flush();
$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\Collection;
use Symfony\Component\Security\Core\User\UserInterface;
class User
class User implements UserInterface
{
private $id;
private $name;
private $username;
private $password;
private $apiToken;
private $roles = [];
private $heroes;
public function __construct()
@ -24,15 +29,18 @@ class User
{
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;
}
@ -49,6 +57,30 @@ class User
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[]
*/
@ -79,4 +111,24 @@ class User
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