diff --git a/.gitignore b/.gitignore index 9acb039..2ad4621 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ ###< doctrine ### /composer.lock - +.php_cs.cache diff --git a/composer.json b/composer.json index 15d37ac..0118cde 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,6 @@ "php": "^7.1.3", "ext-iconv": "*", "ext-pdo_pgsql": "*", - "friendsofsymfony/oauth-server-bundle": "^1.6", "friendsofsymfony/rest-bundle": "^2.3", "jms/serializer-bundle": "^2.3", @@ -13,6 +12,7 @@ "sensio/framework-extra-bundle": "^5.1", "stof/doctrine-extensions-bundle": "^1.3", "symfony/console": "^4.0", + "symfony/debug-pack": "^1.0", "symfony/flex": "^1.0", "symfony/form": "^4.0", "symfony/framework-bundle": "^4.0", diff --git a/config/bundles.php b/config/bundles.php index 2e9a844..c01e652 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -1,19 +1,29 @@ ['all' => true], - Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], - Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], - Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], - Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], - Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], - Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], - FOS\OAuthServerBundle\FOSOAuthServerBundle::class => ['all' => true], - FOS\RestBundle\FOSRestBundle::class => ['all' => true], - JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], - Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], + Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], + Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], + Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], + Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], + Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], + Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], + Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], + FOS\OAuthServerBundle\FOSOAuthServerBundle::class => ['all' => true], + FOS\RestBundle\FOSRestBundle::class => ['all' => true], + JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], + Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], - Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], - WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle::class => ['all' => true], + Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], + WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle::class => ['all' => true], + Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], + Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], ]; diff --git a/config/packages/dev/easy_log_handler.yaml b/config/packages/dev/easy_log_handler.yaml new file mode 100644 index 0000000..27bfc60 --- /dev/null +++ b/config/packages/dev/easy_log_handler.yaml @@ -0,0 +1,16 @@ +services: + EasyCorp\EasyLog\EasyLogHandler: + public: false + arguments: ['%kernel.logs_dir%/%kernel.environment%.log'] + +#// FIXME: How to add this configuration automatically without messing up with the monolog configuration? +#monolog: +# handlers: +# buffered: +# type: buffer +# handler: easylog +# channels: ['!event'] +# level: debug +# easylog: +# type: service +# id: EasyCorp\EasyLog\EasyLogHandler diff --git a/config/packages/dev/monolog.yaml b/config/packages/dev/monolog.yaml new file mode 100644 index 0000000..3662592 --- /dev/null +++ b/config/packages/dev/monolog.yaml @@ -0,0 +1,19 @@ +monolog: + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + channels: ["!event"] + # uncomment to get logging in your browser + # you may have to allow bigger header sizes in your Web server configuration + #firephp: + # type: firephp + # level: info + #chromephp: + # type: chromephp + # level: info + console: + type: console + process_psr_3_messages: false + channels: ["!event", "!doctrine", "!console"] diff --git a/config/packages/fos_oauth_server.yml b/config/packages/fos_oauth_server.yml index 75c9f8e..4c9c0fb 100644 --- a/config/packages/fos_oauth_server.yml +++ b/config/packages/fos_oauth_server.yml @@ -5,4 +5,5 @@ fos_oauth_server: refresh_token_class: PiaApi\Entity\Oauth\RefreshToken auth_code_class: PiaApi\Entity\Oauth\AuthCode service: - user_provider: PiaApi\Auth\UserProvider \ No newline at end of file + user_provider: PiaApi\Auth\UserProvider + storage: PiaApi\Auth\OAuthStorage \ No newline at end of file diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 951f288..2da5e23 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -8,6 +8,7 @@ framework: # Remove or comment this section to explicitly disable session support. session: handler_id: ~ + cookie_lifetime: 86400 # 24 hours #esi: true #fragments: true diff --git a/config/packages/nelmio_cors.yaml b/config/packages/nelmio_cors.yaml index 0e04c0b..9df58e9 100644 --- a/config/packages/nelmio_cors.yaml +++ b/config/packages/nelmio_cors.yaml @@ -4,6 +4,12 @@ nelmio_cors: allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] allow_headers: ['Content-Type', 'Authorization'] - max_age: 3600 + max_age: 86400 # 24 hours paths: - '^/': ~ + '^/pias': + allow_credentials: false + origin_regex: true + allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] + allow_headers: ['Content-Type', 'Authorization'] + allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] + max_age: 86400 # 24 hours diff --git a/config/packages/prod/monolog.yaml b/config/packages/prod/monolog.yaml new file mode 100644 index 0000000..90e1a4c --- /dev/null +++ b/config/packages/prod/monolog.yaml @@ -0,0 +1,17 @@ +monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + excluded_404s: + # regex: exclude all 404 errors from the logs + - ^/ + nested: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + console: + type: console + process_psr_3_messages: false + channels: ["!event", "!doctrine"] diff --git a/config/packages/test/monolog.yaml b/config/packages/test/monolog.yaml new file mode 100644 index 0000000..2762653 --- /dev/null +++ b/config/packages/test/monolog.yaml @@ -0,0 +1,7 @@ +monolog: + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + channels: ["!event"] diff --git a/config/routes.yaml b/config/routes.yaml index dfff478..853c4ac 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,5 +1,6 @@ fos_oauth_server_token: resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml" + methods: [GET, POST, OPTIONS] fos_oauth_server_authorize: resource: "@FOSOAuthServerBundle/Resources/config/routing/authorize.xml" \ No newline at end of file diff --git a/src/Auth/OAuthStorage.php b/src/Auth/OAuthStorage.php new file mode 100644 index 0000000..67e0070 --- /dev/null +++ b/src/Auth/OAuthStorage.php @@ -0,0 +1,58 @@ +userProvider = $piaUserProver; + } + + public function createAccessToken($tokenString, IOAuth2Client $client, $data, $expires, $scope = null) + { + /** @var User $user */ + $user = $data; + + if ($client !== $user->getApplication()) { + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, sprintf('User « %s » cannot access application « %s »', $user->getUsername(), $client->getName())); + } + parent::createAccessToken($tokenString, $client, $data, $expires, $scope); + } +} diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 5a11c53..30bd6ca 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -156,6 +156,8 @@ public function addUserAction(Request $request) $encoder = $this->encoderFactory->getEncoder($user); $user->setPassword($encoder->encodePassword($userData['password'], $user->getSalt())); + $user->setApplication($userData['application']); + $this->getDoctrine()->getManager()->persist($user); $this->getDoctrine()->getManager()->flush(); diff --git a/src/Entity/Oauth/Client.php b/src/Entity/Oauth/Client.php index f4667db..4bb50bc 100644 --- a/src/Entity/Oauth/Client.php +++ b/src/Entity/Oauth/Client.php @@ -1,9 +1,19 @@ users = new ArrayCollection(); } /** diff --git a/src/Entity/Oauth/User.php b/src/Entity/Oauth/User.php index ebed677..bfce237 100644 --- a/src/Entity/Oauth/User.php +++ b/src/Entity/Oauth/User.php @@ -84,6 +84,13 @@ class User implements AdvancedUserInterface, \Serializable */ protected $locked = false; + /** + * @ORM\ManyToOne(targetEntity="Client", inversedBy="users") + * + * @var Client + */ + protected $application; + public function __construct(?string $email = null, ?string $password) { $this->email = $email; @@ -196,6 +203,7 @@ public function serialize() $this->expirationDate, $this->enabled, $this->locked, + $this->application, )); } @@ -211,7 +219,8 @@ public function unserialize($serialized) $this->creationDate, $this->expirationDate, $this->enabled, - $this->locked) = unserialize($serialized); + $this->locked, + $this->application) = unserialize($serialized); } /** @@ -295,4 +304,20 @@ public function isCredentialsNonExpired(): bool { return true; } + + /** + * @return Client + */ + public function getApplication(): ?Client + { + return $this->application; + } + + /** + * @param Client $application + */ + public function setApplication(?Client $application): void + { + $this->application = $application; + } } diff --git a/src/Form/Applications/Transformer/ApplicationTransformer.php b/src/Form/Applications/Transformer/ApplicationTransformer.php new file mode 100644 index 0000000..80a071c --- /dev/null +++ b/src/Form/Applications/Transformer/ApplicationTransformer.php @@ -0,0 +1,42 @@ +doctrine = $doctrine; + } + + public function transform($value) + { + if ($value instanceof Client) { + return $value->getId(); + } + + return -1; + } + + public function reverseTransform($value) + { + return $this->doctrine->getManager('default')->getRepository(Client::class)->find($value); + } +} diff --git a/src/Form/User/CreateUserForm.php b/src/Form/User/CreateUserForm.php index 525e24d..a721841 100644 --- a/src/Form/User/CreateUserForm.php +++ b/src/Form/User/CreateUserForm.php @@ -1,5 +1,13 @@ 'ROLE_USER', 'ROLE_SUPER_ADMIN' => 'ROLE_SUPER_ADMIN', ]; + public function __construct(RegistryInterface $doctrine, ApplicationTransformer $applicationTransformer) + { + $this->doctrine = $doctrine; + $this->applicationTransformer = $applicationTransformer; + } + public function buildForm(FormBuilderInterface $builder, array $options) { $builder + ->add('application', ChoiceType::class, [ + 'required' => true, + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->getApplications(), + ]) ->add('email', EmailType::class) ->add('password', PasswordType::class) ->add('roles', ChoiceType::class, [ 'required' => false, 'multiple' => true, 'expanded' => true, - 'choices' => $this->userRoles + 'choices' => $this->userRoles, ]) ->add('submit', SubmitType::class, [ 'attr' => [ - 'class' => 'fluid' + 'class' => 'fluid', ], - 'label' => 'Créer l\'utilisateur' + 'label' => 'Créer l\'utilisateur', ]) ; + + $builder->get('application')->addModelTransformer($this->applicationTransformer); + } + + private function getApplications(): array + { + $applications = []; + + foreach ($this->doctrine->getManager('default')->getRepository(Client::class)->findAll() as $application) { + $applications[$application->getId()] = $application->getName(); + } + + return array_flip($applications); } } diff --git a/symfony.lock b/symfony.lock index 458be33..ec8e6cb 100644 --- a/symfony.lock +++ b/symfony.lock @@ -59,6 +59,15 @@ "doctrine/orm": { "version": "v2.6.1" }, + "easycorp/easy-log-handler": { + "version": "1.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "1.0", + "ref": "70062abc2cd58794d2a90274502f81b55cd9951b" + } + }, "friendsofsymfony/oauth-server-bundle": { "version": "1.6.1" }, @@ -98,6 +107,9 @@ "ref": "fe60ce509ef04a3f40da96e3979bc8d9b13b2372" } }, + "monolog/monolog": { + "version": "1.23.0" + }, "nelmio/cors-bundle": { "version": "1.5", "recipe": { @@ -173,6 +185,18 @@ "symfony/debug": { "version": "v4.0.8" }, + "symfony/debug-bundle": { + "version": "3.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "3.3", + "ref": "71d29aaf710fd59cd3abff2b1ade907ed73103c6" + } + }, + "symfony/debug-pack": { + "version": "v1.0.5" + }, "symfony/dependency-injection": { "version": "v4.0.8" }, @@ -227,6 +251,18 @@ "symfony/lts": { "version": "4-dev" }, + "symfony/monolog-bridge": { + "version": "v4.0.9" + }, + "symfony/monolog-bundle": { + "version": "3.1", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "3.1", + "ref": "371d1a2b69984710646b09a1182ef1d4308c904f" + } + }, "symfony/options-resolver": { "version": "v4.0.8" }, diff --git a/templates/Applications/manageApplications.html.twig b/templates/Applications/manageApplications.html.twig index 99991e7..ec1693b 100644 --- a/templates/Applications/manageApplications.html.twig +++ b/templates/Applications/manageApplications.html.twig @@ -158,6 +158,7 @@ modal.find('.actions').remove(); $('.ui.checkbox').checkbox(); + $('.ui.dropdown').dropdown(); modal.modal('show'); } diff --git a/templates/User/createForm.html.twig b/templates/User/createForm.html.twig index a85a7d3..1b90202 100644 --- a/templates/User/createForm.html.twig +++ b/templates/User/createForm.html.twig @@ -2,6 +2,7 @@ {{ form_row(form.username) }} {{ form_row(form.email) }} +{{ form_row(form.application) }} {{ form_row(form.roles) }} {{ form_row(form.expirationDate) }} {{ form_row(form.enabled) }} diff --git a/templates/User/manageUsers.html.twig b/templates/User/manageUsers.html.twig index b680129..74a6876 100644 --- a/templates/User/manageUsers.html.twig +++ b/templates/User/manageUsers.html.twig @@ -39,6 +39,7 @@ # + Application Username Email Roles @@ -56,6 +57,15 @@ {% for user in users %} {{ user.id }} + + {% if user.application is not null %} + + {{ user.application.name }} + + {% else %} + N/A + {% endif %} + {{ user.username }} {{ user.email }} @@ -136,6 +146,7 @@ modal.find('.actions').remove(); $('.ui.checkbox').checkbox(); + $('.ui.dropdown').dropdown(); modal.modal('show'); } diff --git a/templates/base.html.twig b/templates/base.html.twig index cf5a7ca..2dc50ea 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -26,6 +26,7 @@