Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion config/packages/fos_oauth_server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ fos_oauth_server:
client_class: App\Authorization\Entity\Oauth2\Client
access_token_class: App\Authorization\Entity\Oauth2\AccessToken
refresh_token_class: App\Authorization\Entity\Oauth2\RefreshToken
auth_code_class: App\Authorization\Entity\Oauth2\AuthCode
auth_code_class: App\Authorization\Entity\Oauth2\AuthCode

service:
user_provider: security.user.provider.concrete.my_database_provider
options:
supported_scopes: user
access_token_lifetime: 3600
11 changes: 10 additions & 1 deletion config/packages/security.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
security:
encoders:
App\Aurora\Domain\User\Entity\User:
algorithm: bcrypt
cost: 12

# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
in_memory: { memory: ~ }
my_database_provider:
entity:
class: App\Aurora\Domain\User\Entity\User
property: username

firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
Expand Down
31 changes: 20 additions & 11 deletions src/Aurora/Domain/Article/ArticleService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Aurora\App\Support\FractalService;
use App\Aurora\Domain\Article\Entity\Article;
use App\Aurora\Domain\User\Entity\User;
use App\Aurora\Infrastructure\Article\ArticleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityNotFoundException;
Expand All @@ -28,10 +29,17 @@ class ArticleService
* @var ArticleTransformer
*/
private $articleTransformer;

/**
* @var RouterInterface
*/
private $router;

/**
* @var ArticleRepository
*/
private $articleRepository;

/**
* @var FractalService
*/
Expand All @@ -43,10 +51,10 @@ public function __construct(
FractalService $fractalService
)
{

$this->entityManager = $entityManager;
$this->articleTransformer = $articleTransformer;
$this->fractalService = $fractalService;
$this->articleRepository = $this->entityManager->getRepository(Article::class);
}

/**
Expand All @@ -59,9 +67,7 @@ public function getArticles(Request $request, RouterInterface $router)
$page = NULL !== $request->get('page') ? (int) $request->get('page') : 1;
$perPage = NULL !== $request->get('per_page') ? (int) $request->get('per_page') : 10;

$articles = $this->entityManager->getRepository(Article::class);

$doctrineAdapter = new DoctrineORMAdapter($articles->getArticles());
$doctrineAdapter = new DoctrineORMAdapter($this->articleRepository->getArticles());
$paginator = new Pagerfanta($doctrineAdapter);
$paginator->setCurrentPage($page);
$paginator->setMaxPerPage($perPage);
Expand All @@ -73,11 +79,13 @@ public function getArticles(Request $request, RouterInterface $router)
$inputParams = $request->attributes->get('_route_params');
$newParams = array_merge($inputParams, $request->query->all());
$newParams['page'] = $page;

return $router->generate($route, $newParams, 0);
});

$resource = new Collection($filteredResults,$this->articleTransformer, 'article');
$resource = new Collection($filteredResults, $this->articleTransformer, 'article');
$resource->setPaginator($paginatorAdapter);

return $resource;
}

Expand All @@ -88,10 +96,11 @@ public function getArticles(Request $request, RouterInterface $router)
*/
public function getArticleById($id)
{
$article = $this->entityManager->getRepository(Article::class)->find($id);
$article = $this->articleRepository->find($id);

if ($article)
if ($article) {
return new Item($article, $this->articleTransformer, 'article');
}

throw new EntityNotFoundException("Article not found");
}
Expand All @@ -112,21 +121,21 @@ public function addArticle(Request $request)
$article->setAuthor($user);
$article->setContributors(new ArrayCollection([$user]));

//set tags
if(is_array($request->request->get('tags'))){
// set tags
if (is_array($request->request->get('tags'))) {
foreach ($request->request->get('tags') as $tag) {
$article->addTagFromName($tag);
}
}

$this->entityManager->getRepository(Article::class)->save($article);
$this->articleRepository->save($article);

}


public function searchArticle(Request $request)
{
$resource = $this->entityManager->getRepository(Article::class)->searchArticle($request);
$resource = $this->articleRepository->searchArticle($request);

$collection = new Collection($resource,$this->articleTransformer,'article');

Expand Down
120 changes: 114 additions & 6 deletions src/Aurora/Domain/User/Entity/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

namespace App\Aurora\Domain\User\Entity;

class User {
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;

class User implements UserInterface, EquatableInterface, \Serializable
{

/**
* @var integer
Expand All @@ -24,10 +28,20 @@ class User {
*/
protected $password;

/**
* @var string
*/
protected $salt;

/**
* @var array
*/
private $roles = [];

/**
* @return int
*/
public function getId()
public function getId(): int
{
return $this->id;
}
Expand All @@ -36,15 +50,15 @@ public function getId()
/**
* @return string
*/
public function getUsername()
public function getUsername(): string
{
return $this->username;
}

/**
* @param string $username
*/
public function setUsername($username)
public function setUsername(string $username): void
{
$this->username = $username;
}
Expand All @@ -60,7 +74,7 @@ public function getEmail(): string
/**
* @param string $email
*/
public function setEmail(string $email)
public function setEmail(string $email): void
{
$this->email = $email;
}
Expand All @@ -76,10 +90,104 @@ public function getPassword(): string
/**
* @param string $password
*/
public function setPassword(string $password)
public function setPassword(string $password): void
{
$this->password = $password;
}

/**
* Returns the salt that was originally used to encode the password.
*
* {@inheritdoc}
*/
public function getSalt(): ?string
{
// See "Do you need to use a Salt?" at https://symfony.com/doc/current/cookbook/security/entity_provider.html
// we're using bcrypt in security.yml to encode the password, so
// the salt value is built-in and you don't have to generate one

return null;
}

/**
* @param string $salt
*/
public function setSalt(string $salt): void
{
$this->salt = $salt;
}

/**
* {@inheritdoc}
*/
public function getRoles(): array
{
$roles = $this->roles;

// guarantees that a user always has at least one role for security
if (empty($roles)) {
$roles[] = 'ROLE_USER';
}

return array_unique($roles);
}

public function setRoles(array $roles): void
{
$this->roles = $roles;
}

/**
* Removes sensitive data from the user.
*
* {@inheritdoc}
*/
public function eraseCredentials(): void
{
// if you had a plainPassword property, you'd nullify it here
// $this->plainPassword = null;
}

/**
* {@inheritdoc}
*/
public function serialize(): string
{
// add $this->salt too if you don't use Bcrypt or Argon2i
return serialize([$this->id, $this->username, $this->password]);
}

/**
* {@inheritdoc}
*/
public function unserialize($serialized): void
{
// add $this->salt too if you don't use Bcrypt or Argon2i
[$this->id, $this->username, $this->password] = unserialize($serialized, ['allowed_classes' => false]);
}

/**
* @param UserInterface $user
* @return bool
*/
public function isEqualTo(UserInterface $user): bool
{
if (!$user instanceof User) {
return false;
}

if ($this->password !== $user->getPassword()) {
return false;
}

if ($this->salt !== $user->getSalt()) {
return false;
}

if ($this->username !== $user->getUsername()) {
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ App\Aurora\Domain\Article\Entity\Article:
joinTable:
name: article_contributors


manyToOne:
author:
targetEntity: App\Aurora\Domain\User\Entity\User
Expand Down
11 changes: 9 additions & 2 deletions src/Aurora/Resources/doctrine/mapping/User.Entity.User.orm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ App\Aurora\Domain\User\Entity\User:
fields:
username:
type: string
length: 100
length: 31
email:
type: string
length: 127
password:
type: string
type: string
salt:
type: string
length: 32
nullable: true
roles:
type: json
30 changes: 29 additions & 1 deletion src/DataFixtures/UserFixtures.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,40 @@
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class UserFixtures extends Fixture implements OrderedFixtureInterface
{
const TEST_USER = 'test-user';

/**
* @var UserPasswordEncoderInterface
*/
private $passwordEncoder;

/**
* @param UserPasswordEncoderInterface $encoder
*/
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->passwordEncoder = $encoder;
}

public function load(ObjectManager $manager)
{
$faker = \Faker\Factory::create();
$salt = $this->generateSalt();

$user = new User();
$user->setUsername($faker->userName);
$user->setEmail("user@example.com");
$user->setPassword(sha1('secret'));
$user->setRoles(['ROLE_USER']);

$plainPassword = 'secret';

// See https://symfony.com/doc/current/book/security.html#security-encoding-password
$encodedPassword = $this->passwordEncoder->encodePassword($user, $plainPassword);
$user->setPassword($encodedPassword);

$manager->persist($user);
$manager->flush();
Expand All @@ -35,4 +57,10 @@ public function getOrder()
return 1;
}

private function generateSalt(): string
{
$bytes = random_bytes(32);

return base_convert(bin2hex($bytes), 16, 36);
}
}
Loading