From afc8424e7145468ee4862cf31c59ef8241899aa7 Mon Sep 17 00:00:00 2001 From: Prytoegrian <5312739+prytoegrian@users.noreply.github.com> Date: Sat, 22 Sep 2018 18:17:27 +0200 Subject: [PATCH] =?UTF-8?q?Connexion=20via=20LDAP=20:=201.=20Pr=C3=A9parat?= =?UTF-8?q?ion=20(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Création du collecteur de configuration * TDD factory service auth * Test AuthController * Avec les bonnes classes... * No more testable AuthenticationController * TDD authentification interne * Combler le défaut de sécurité sur la vacuité d'une clé * 100% CC InterneAuthentifier * Ménage --- Absence/Type/TypeRepository.php | 2 +- Groupe/Employe/EmployeRepository.php | 2 +- .../GrandResponsableRepository.php | 2 +- Groupe/GroupeRepository.php | 2 +- Groupe/Responsable/ResponsableRepository.php | 2 +- Journal/JournalRepository.php | 2 +- Makefile | 3 + Planning/Creneau/CreneauRepository.php | 6 +- Planning/PlanningRepository.php | 2 +- Public/index.php | 11 ++ .../AuthentificationController.php | 122 ------------------ .../Units/Tools/Libraries/ARestController.php | 8 -- .../Services/AAuthentifierFactoryService.php | 64 +++++++++ .../Services/InterneAuthentifierService.php | 94 ++++++++++++++ .../AuthentificationController.php | 39 +++--- .../AuthentificationFailedException.php | 16 +++ Tools/Exceptions/BadRequestException.php | 17 +++ Tools/Libraries/StorageConfiguration.php | 75 +++++++++++ .../Services/AAuthentifierFactoryService.php | 63 +++++++++ Tools/Services/InterneAuthentifierService.php | 57 ++++++++ Tools/Services/LdapAuthentifierService.php | 20 +++ Utilisateur/UtilisateurRepository.php | 28 ++-- configuration.json.example | 19 ++- di-config.php | 15 ++- 24 files changed, 498 insertions(+), 173 deletions(-) delete mode 100644 Tests/Units/Tools/Controllers/AuthentificationController.php create mode 100644 Tests/Units/Tools/Services/AAuthentifierFactoryService.php create mode 100644 Tests/Units/Tools/Services/InterneAuthentifierService.php create mode 100644 Tools/Exceptions/AuthentificationFailedException.php create mode 100644 Tools/Exceptions/BadRequestException.php create mode 100644 Tools/Libraries/StorageConfiguration.php create mode 100644 Tools/Services/AAuthentifierFactoryService.php create mode 100644 Tools/Services/InterneAuthentifierService.php create mode 100644 Tools/Services/LdapAuthentifierService.php diff --git a/Absence/Type/TypeRepository.php b/Absence/Type/TypeRepository.php index 6471c485..41f65630 100644 --- a/Absence/Type/TypeRepository.php +++ b/Absence/Type/TypeRepository.php @@ -95,7 +95,7 @@ final protected function getEntite2Storage(AEntite $entite) : array */ final protected function setWhere(array $parametres) { - if (!empty($parametres['id'])) { + if (array_key_exists('id', $parametres)) { $this->queryBuilder->andWhere('ta_id = :id'); $this->queryBuilder->setParameter(':id', (int) $parametres['id']); } diff --git a/Groupe/Employe/EmployeRepository.php b/Groupe/Employe/EmployeRepository.php index f3bf0b7e..f72ed547 100644 --- a/Groupe/Employe/EmployeRepository.php +++ b/Groupe/Employe/EmployeRepository.php @@ -103,7 +103,7 @@ public function deleteOne(int $id) : int */ final protected function setWhere(array $parametres) { - if (!empty($parametres['id'])) { + if (array_key_exists('id', $parametres)) { $this->queryBuilder->andWhere('gu_gid = :id'); $this->queryBuilder->setParameter(':id', (int) $parametres['id']); } diff --git a/Groupe/GrandResponsable/GrandResponsableRepository.php b/Groupe/GrandResponsable/GrandResponsableRepository.php index 5b642c29..e532b798 100644 --- a/Groupe/GrandResponsable/GrandResponsableRepository.php +++ b/Groupe/GrandResponsable/GrandResponsableRepository.php @@ -103,7 +103,7 @@ public function deleteOne(int $id) : int */ final protected function setWhere(array $parametres) { - if (!empty($parametres['id'])) { + if (array_key_exists('id', $parametres)) { $this->queryBuilder->andWhere('ggr_gid = :id'); $this->queryBuilder->setParameter(':id', (int) $parametres['id']); } diff --git a/Groupe/GroupeRepository.php b/Groupe/GroupeRepository.php index ad1a13ac..1abb8039 100644 --- a/Groupe/GroupeRepository.php +++ b/Groupe/GroupeRepository.php @@ -76,7 +76,7 @@ final protected function setSet(array $parametres) */ final protected function setWhere(array $parametres) { - if (!empty($parametres['id'])) { + if (array_key_exists('id', $parametres)) { $this->queryBuilder->andWhere('g_gid = :id'); $this->queryBuilder->setParameter(':id', (int) $parametres['id']); } diff --git a/Groupe/Responsable/ResponsableRepository.php b/Groupe/Responsable/ResponsableRepository.php index 442be57e..4d972096 100644 --- a/Groupe/Responsable/ResponsableRepository.php +++ b/Groupe/Responsable/ResponsableRepository.php @@ -103,7 +103,7 @@ public function deleteOne(int $id) : int */ final protected function setWhere(array $parametres) { - if (!empty($parametres['id'])) { + if (array_key_exists('id', $parametres)) { $this->queryBuilder->andWhere('g_gid = :id'); $this->queryBuilder->setParameter(':id', (int) $parametres['id']); } diff --git a/Journal/JournalRepository.php b/Journal/JournalRepository.php index 25675c02..f60df5b0 100644 --- a/Journal/JournalRepository.php +++ b/Journal/JournalRepository.php @@ -105,7 +105,7 @@ public function deleteOne(int $id) : int */ final protected function setWhere(array $parametres) { - if (!empty($parametres['id'])) { + if (array_key_exists('id', $parametres)) { $this->queryBuilder->andWhere('log_id = :id'); $this->queryBuilder->setParameter(':id', (int) $parametres['id']); } diff --git a/Makefile b/Makefile index 1390e303..8d9f2fd4 100644 --- a/Makefile +++ b/Makefile @@ -24,3 +24,6 @@ minor: patch: $(call make_version,patch) + +test: + Vendor/Bin/atoum -ulr diff --git a/Planning/Creneau/CreneauRepository.php b/Planning/Creneau/CreneauRepository.php index 71093a41..a6b458df 100644 --- a/Planning/Creneau/CreneauRepository.php +++ b/Planning/Creneau/CreneauRepository.php @@ -34,7 +34,7 @@ final protected function getEntiteClass() : string final protected function getParamsConsumer2Storage(array $paramsConsumer) : array { $results = []; - if (!empty($paramsConsumer['planningId'])) { + if (array_key_exists('planningId', $paramsConsumer)) { $results['planning_id'] = (int) $paramsConsumer['planningId']; } @@ -144,11 +144,11 @@ final protected function setSet(array $parametres) */ final protected function setWhere(array $parametres) { - if (!empty($parametres['id'])) { + if (array_key_exists('id', $parametres)) { $this->queryBuilder->andWhere('creneau_id = :id'); $this->queryBuilder->setParameter(':id', (int) $parametres['id']); } - if (!empty($parametres['planning_id'])) { + if (array_key_exists('planning_id', $parametres)) { $this->queryBuilder->andWhere('planning_id = :planningId'); $this->queryBuilder->setParameter(':planningId', (int) $parametres['planning_id']); } diff --git a/Planning/PlanningRepository.php b/Planning/PlanningRepository.php index d3e54802..e8338a53 100644 --- a/Planning/PlanningRepository.php +++ b/Planning/PlanningRepository.php @@ -84,7 +84,7 @@ final protected function setSet(array $parametres) */ final protected function setWhere(array $parametres) { - if (!empty($parametres['id'])) { + if (array_key_exists('id', $parametres)) { $this->queryBuilder->andWhere('planning_id = :id'); $this->queryBuilder->setParameter(':id', $parametres['id']); } diff --git a/Public/index.php b/Public/index.php index 64264033..f40f5309 100644 --- a/Public/index.php +++ b/Public/index.php @@ -19,6 +19,17 @@ $containerBuilder->addDefinitions(ROOT_PATH . 'di-config.php'); $app = new \Slim\App($containerBuilder->build()); +/* + * Commençons simple et posons le paramétrage des assert ici. + * Précision : une partie est faite côté php.ini (nouvelle norme) + */ +if ('development' == $app->getContainer()->get('environment')->get('stage')) { + ini_set('assert.exception', '1'); +} else { + assert_options(ASSERT_ACTIVE, 0); + ini_set('assert.exception', '0'); +} + /* * /!\ Les Middlewares sont executés en mode PILE : le premier de la liste est lancé en dernier */ diff --git a/Tests/Units/Tools/Controllers/AuthentificationController.php b/Tests/Units/Tools/Controllers/AuthentificationController.php deleted file mode 100644 index 21b59f8b..00000000 --- a/Tests/Units/Tools/Controllers/AuthentificationController.php +++ /dev/null @@ -1,122 +0,0 @@ - - * @author Wouldsmina - * - * @since 0.2 - */ -final class AuthentificationController extends \LibertAPI\Tests\Units\Tools\Libraries\AController -{ - protected function initRepository() - { - $this->mockGenerator->orphanize('__construct'); - $this->repository = new \mock\LibertAPI\Utilisateur\UtilisateurRepository(); - } - - protected function initEntite() - { - $this->mockGenerator->orphanize('__construct'); - $this->entite = new \mock\LibertAPI\Utilisateur\UtilisateurEntite(); - $this->entite->getMockController()->getId = 42; - $this->entite->getMockController()->getToken = 12; - $this->entite->getMockController()->getLogin = 12; - $this->entite->getMockController()->getNom = 12; - $this->entite->getMockController()->getDateInscription = 12; - $this->entite->getMockController()->getMotDePasse = 'Fatboy slim'; - } - - /************************************************* - * GET - *************************************************/ - - /** - * Teste la méthode avec un mauvais header - */ - public function testGetBadAuthentificationMechanism() - { - // Le framework fait du traitement, un mauvais json est simplement null - $this->request->getMockController()->getHeaderLine = 'NotBasic'; - $this->newTestedInstance($this->repository, $this->router); - - $response = $this->testedInstance->get($this->request, $this->response, []); - - $this->assertFail($response, 400); - } - - /** - * Teste la méthode get d'une authentification non réussie - */ - public function testGetNotFound() - { - $this->repository->getMockController()->find = function () { - throw new \UnexpectedValueException(''); - }; - $this->request->getMockController()->getHeaderLine = 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'; - $this->newTestedInstance($this->repository, $this->router); - - $response = $this->testedInstance->get( - $this->request, - $this->response, - [] - ); - - $this->assertFail($response, 404); - } - - /** - * Teste la méthode get d'une authentification échouée avec mauvais mdp - */ - public function testGetWrongPassword() - { - $this->entite->getMockController()->isPasswordMatching = false; - $this->repository->getMockController()->find = $this->entite; - $this->request->getMockController()->getHeaderLine = 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'; - $this->newTestedInstance($this->repository, $this->router); - - $response = $this->testedInstance->get( - $this->request, - $this->response, - [] - ); - - $this->assertFail($response, 404); - } - - /** - * Teste la méthode get d'une authentification réussie - */ - public function testGetFound() - { - $token = 'abcde'; - $this->entite->getMockController()->getToken = $token; - $this->entite->getMockController()->isPasswordMatching = true; - $this->repository->getMockController()->find = $this->entite; - $this->repository->getMockController()->regenerateToken = $this->entite; - $this->request->getMockController()->getHeaderLine = 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'; - $this->newTestedInstance($this->repository, $this->router); - - $response = $this->testedInstance->get($this->request, $this->response, []); - $data = $this->getJsonDecoded($response->getBody()); - - $this->integer($response->getStatusCode())->isIdenticalTo(200); - $this->array($data) - ->integer['code']->isIdenticalTo(200) - ->string['status']->isIdenticalTo('success') - ->string['data']->isIdenticalTo($token) - ; - } - - protected function getOne() - { - return $this->testedInstance->get($this->request, $this->response, ['utilisateurId' => 99,]); - } - - protected function getList() - { - return $this->testedInstance->get($this->request, $this->response, []); - } -} diff --git a/Tests/Units/Tools/Libraries/ARestController.php b/Tests/Units/Tools/Libraries/ARestController.php index a1dc1cbd..e83bf222 100644 --- a/Tests/Units/Tools/Libraries/ARestController.php +++ b/Tests/Units/Tools/Libraries/ARestController.php @@ -29,14 +29,6 @@ abstract class ARestController extends AController */ protected $currentAdmin; - /** - * Init des tests - */ - public function beforeTestMethod($method) - { - parent::beforeTestMethod($method); - } - /************************************************* * GET *************************************************/ diff --git a/Tests/Units/Tools/Services/AAuthentifierFactoryService.php b/Tests/Units/Tools/Services/AAuthentifierFactoryService.php new file mode 100644 index 00000000..9451ca4f --- /dev/null +++ b/Tests/Units/Tools/Services/AAuthentifierFactoryService.php @@ -0,0 +1,64 @@ + + * @author Wouldsmina + * + * @since 1.1 + */ +class AAuthentifierFactoryService extends \Atoum +{ + public function beforeTestMethod($method) + { + parent::beforeTestMethod($method); + + $this->mockGenerator->orphanize('__construct'); + $this->mockGenerator->shuntParentClassCalls(); + $this->configuration = new \mock\LibertAPI\Tools\Libraries\StorageConfiguration(); + $this->mockGenerator->orphanize('__construct'); + $this->repository = new \mock\LibertAPI\Tools\Libraries\ARepository(); + } + + public function testGetLdapService() + { + $this->calling($this->configuration)->getHowToConnectUser = 'ldap'; + + $testedClass = $this->testedClass->getClass(); + $service = $testedClass::getAuthentifier($this->configuration, $this->repository); + $this->object($service)->isInstanceOf(Services\LdapAuthentifierService::class); + } + + public function testGetInterneService() + { + $this->calling($this->configuration)->getHowToConnectUser = 'dbconges'; + + $testedClass = $this->testedClass->getClass(); + $service = $testedClass::getAuthentifier($this->configuration, $this->repository); + $this->object($service)->isInstanceOf(Services\InterneAuthentifierService::class); + } + + public function testGetUnknownService() + { + $this->calling($this->configuration)->getHowToConnectUser = 'foobar'; + + $this->exception(function () { + $testedClass = $this->testedClass->getClass(); + $testedClass::getAuthentifier($this->configuration, $this->repository); + })->isInstanceOf(\UnexpectedValueException::class); + } + + /** + * @var LibertAPI\Tools\Libraries\StorageConfiguration Mock de la configuration + */ + private $configuration; + + /** + * @var LibertAPI\Tools\Libraries\ARepository Mock d'un repository lambda + */ + private $repository; +} diff --git a/Tests/Units/Tools/Services/InterneAuthentifierService.php b/Tests/Units/Tools/Services/InterneAuthentifierService.php new file mode 100644 index 00000000..9acc9a1d --- /dev/null +++ b/Tests/Units/Tools/Services/InterneAuthentifierService.php @@ -0,0 +1,94 @@ + + * @author Wouldsmina + * + * @since 1.1 + */ +class InterneAuthentifierService extends \Atoum +{ + /** + * Init des tests + */ + public function beforeTestMethod($method) + { + parent::beforeTestMethod($method); + + $this->mockGenerator->orphanize('__construct'); + $this->repository = new \mock\LibertAPI\Tools\Libraries\ARepository(); + $this->mockGenerator->orphanize('__construct'); + $this->mockGenerator->shuntParentClassCalls(); + $this->request = new \mock\Slim\Http\Request(); + } + + public function testIsAuthentificationSucceedBadRequest() + { + $this->calling($this->request)->getHeaderLine = 'Lollipop'; + + $this->exception(function() { + $this->newTestedInstance($this->repository); + $this->testedInstance->isAuthentificationSucceed($this->request); + })->isInstanceOf(BadRequestException::class); + } + + public function testIsAuthentificationSucceedFalse() + { + $this->calling($this->request)->getHeaderLine = 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'; + $entite = new \LibertAPI\Utilisateur\UtilisateurEntite([ + 'id' => 42, + 'password' => md5('Fatboy slim'), + ]); + $this->calling($this->repository)->find = $entite; + $this->newTestedInstance($this->repository); + $succeed = $this->testedInstance->isAuthentificationSucceed($this->request); + + $this->boolean($succeed)->isFalse(); + } + + public function testIsAuthentificationSucceedTrue() + { + $this->calling($this->request)->getHeaderLine = 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'; + $entite = new \LibertAPI\Utilisateur\UtilisateurEntite([ + 'id' => 42, + 'password' => md5('OpenSesame'), + ]); + $this->calling($this->repository)->find = $entite; + $this->newTestedInstance($this->repository); + $succeed = $this->testedInstance->isAuthentificationSucceed($this->request); + + $this->boolean($succeed)->isTrue(); + } + + public function testGetLogin() + { + $this->newTestedInstance($this->repository); + $this->string($this->testedInstance->getLogin())->isEmpty(); + + $this->calling($this->request)->getHeaderLine = 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'; + $entite = new \LibertAPI\Utilisateur\UtilisateurEntite([ + 'id' => 42, + 'password' => md5('Fatboy slim'), + ]); + $this->calling($this->repository)->find = $entite; + $this->testedInstance->isAuthentificationSucceed($this->request); + + $this->string($this->testedInstance->getLogin())->isIdenticalTo('Aladdin'); + } + + /** + * @var LibertAPI\Tools\Libraries\ARepository Mock d'un repository lambda + */ + private $repository; + + /** + * @var \Slim\Http\Request Mock de la requête HTTP + */ + protected $request; +} diff --git a/Tools/Controllers/AuthentificationController.php b/Tools/Controllers/AuthentificationController.php index b017e860..44443675 100644 --- a/Tools/Controllers/AuthentificationController.php +++ b/Tools/Controllers/AuthentificationController.php @@ -2,6 +2,10 @@ namespace LibertAPI\Tools\Controllers; use LibertAPI\Utilisateur\UtilisateurRepository; +use LibertAPI\Tools\Exceptions\BadRequestException; +use LibertAPI\Tools\Exceptions\AuthentificationFailedException; +use LibertAPI\Tools\Libraries\StorageConfiguration; +use LibertAPI\Tools\Services\AAuthentifierFactoryService; use Slim\Interfaces\RouterInterface as IRouter; use LibertAPI\Tools\Interfaces; use Psr\Http\Message\ServerRequestInterface as IRequest; @@ -10,6 +14,8 @@ /** * Contrôleur de l'authentification * + * Depuis la 1.1, n'est plus testable unitairement (les multiples authentifiers et la fabrique l'empêchent). À tester par des tests de services + * * @author Prytoegrian * @author Wouldsmina * @@ -18,9 +24,10 @@ final class AuthentificationController extends \LibertAPI\Tools\Libraries\AController implements Interfaces\IGetable { - public function __construct(\LibertAPI\Utilisateur\UtilisateurRepository $repository, IRouter $router) + public function __construct(UtilisateurRepository $repository, IRouter $router, StorageConfiguration $configuration) { parent::__construct($repository, $router); + $this->configuration = $configuration; } /** @@ -28,32 +35,34 @@ public function __construct(\LibertAPI\Utilisateur\UtilisateurRepository $reposi */ public function get(IRequest $request, IResponse $response, array $arguments) : IResponse { - $authentificationType = 'Basic'; - $authentification = $request->getHeaderLine('Authorization'); - if (0 !== stripos($authentification, $authentificationType)) { - return $this->getResponseBadRequest($response, 'Authorization mechanism is not set to « ' . $authentificationType . ' »'); - } - - $authentification = substr($authentification, strlen($authentificationType) + 1); - list($login, $password) = explode(':', base64_decode($authentification)); - try { + $authentifier = AAuthentifierFactoryService::getAuthentifier($this->configuration, $this->repository); + if (!$authentifier->isAuthentificationSucceed($request)) { + throw new AuthentificationFailedException(); + } $utilisateur = $this->repository->find([ - 'login' => $login, + 'login' => $authentifier->getLogin(), 'isActif' => true, ]); - if (!$utilisateur->isPasswordMatching($password)) { - throw new \UnexpectedValueException('Wrong password'); - } - $utilisateurUpdated = $this->repository->regenerateToken($utilisateur); + } catch (BadRequestException $e) { + return $this->getResponseBadRequest($response, 'Authorization header doesn\'t comply to authentication method'); + } catch (AuthentificationFailedException $e) { + return $this->getResponseNotFound($response, 'No user matches these criteria'); } catch (\UnexpectedValueException $e) { return $this->getResponseNotFound($response, 'No user matches these criteria'); } + $utilisateurUpdated = $this->repository->regenerateToken($utilisateur); + return $this->getResponseSuccess( $response, $utilisateurUpdated->getToken(), 200 ); } + + /** + * @var StorageConfiguration + */ + private $configuration; } diff --git a/Tools/Exceptions/AuthentificationFailedException.php b/Tools/Exceptions/AuthentificationFailedException.php new file mode 100644 index 00000000..27180407 --- /dev/null +++ b/Tools/Exceptions/AuthentificationFailedException.php @@ -0,0 +1,16 @@ + + * @author Wouldsmina + * + * @since 1.1 + * Ne devrait être contacté que par AuthentificationController + * Ne devrait contacter personne + */ +class AuthentificationFailedException extends \UnexpectedValueException +{ +} diff --git a/Tools/Exceptions/BadRequestException.php b/Tools/Exceptions/BadRequestException.php new file mode 100644 index 00000000..f8b08393 --- /dev/null +++ b/Tools/Exceptions/BadRequestException.php @@ -0,0 +1,17 @@ + + * @author Wouldsmina + * + * @since 1.1 + * + * Ne devrait être contacté que par Tools\Services\*AuthentifierService + * Ne devrait contacter personne + */ +class BadRequestException extends \RuntimeException +{ +} diff --git a/Tools/Libraries/StorageConfiguration.php b/Tools/Libraries/StorageConfiguration.php new file mode 100644 index 00000000..40cd5ff1 --- /dev/null +++ b/Tools/Libraries/StorageConfiguration.php @@ -0,0 +1,75 @@ + + * @author Wouldsmina + * + * @since 1.1 + */ +class StorageConfiguration +{ + private $data; + + public function __construct(Connection $storageConnector) + { + $req = 'SELECT * FROM conges_config ORDER BY conf_groupe'; + $res = $storageConnector->query($req); + foreach ($res->fetchAll(\PDO::FETCH_ASSOC) as $value) { + $groupe = $value['conf_groupe']; + $nom = $value['conf_nom']; + $this->data[$groupe][$nom] = [ + 'valeur' => $value['conf_valeur'], + 'type' => $value['conf_type'], + ]; + } + } + + public function getHowToConnectUser() : string + { + return $this->getGroupeAuthentificationValeur('how_to_connect_user'); + } + + public function isUsersExportFromLdap() : bool + { + return $this->getGroupeAuthentificationValeur('export_users_from_ldap'); + } + + /** + * Retourne une valeur du groupe d'authentification par son nom + * + * @param string $nom + * + * @return mixed + */ + private function getGroupeAuthentificationValeur($nom) + { + return $this->getValeur($nom, '04_Authentification'); + } + + /** + * Retourne la valeur d'une configuration en fonction de son groupe et de son nom + * + * @param string $nom + * @param string $groupe + * + * @return mixed + * @require that $nom et $groupe sont des offsets connus + */ + private function getValeur($nom, $groupe) { + assert(isset($this->data[$groupe]) && isset($this->data[$groupe][$nom])); + + $config = $this->data[$groupe][$nom]; + + if ('boolean' === $config['type']) { + return 'TRUE' === $config['valeur']; + } + + return $config['valeur']; + } + +} diff --git a/Tools/Services/AAuthentifierFactoryService.php b/Tools/Services/AAuthentifierFactoryService.php new file mode 100644 index 00000000..f359b150 --- /dev/null +++ b/Tools/Services/AAuthentifierFactoryService.php @@ -0,0 +1,63 @@ + + * @author Wouldsmina + * + * @since 1.1 + */ +abstract class AAuthentifierFactoryService +{ + /** + * Retourne la bonne implémentation du service d'authentification en fonction des paramètres transmis + */ + final public static function getAuthentifier(StorageConfiguration $configuration, ARepository $repository) : self + { + switch ($configuration->getHowToConnectUser()) { + case 'ldap': + return new LdapAuthentifierService($repository); + case 'dbconges': + return new InterneAuthentifierService($repository); + default: + throw new \UnexpectedValueException("Unknown Service"); + } + } + + public function __construct(ARepository $repository) + { + $this->repository = $repository; + } + + /** + * Contrat standard des services d'authentification + * @return true si l'authentification s'est bien déroulée + * @throws BadRequestException Si la requête n'est pas bien formée + */ + abstract public function isAuthentificationSucceed(IRequest $request) : bool; + + /** + * Retourne le login de l'utilisateur pour être consommé par la DB + */ + abstract public function getLogin() : string; + + protected function getRepository() : ARepository + { + return $this->repository; + } + + /** + * @var ARepository Repository utilisateur + */ + private $repository; +} diff --git a/Tools/Services/InterneAuthentifierService.php b/Tools/Services/InterneAuthentifierService.php new file mode 100644 index 00000000..efde361f --- /dev/null +++ b/Tools/Services/InterneAuthentifierService.php @@ -0,0 +1,57 @@ + + * @author Wouldsmina + * + * @since 1.1 + */ +class InterneAuthentifierService extends AAuthentifierFactoryService +{ + public function __construct(ARepository $repository) + { + parent::__construct($repository); + } + + /** + * {@inheritDoc} + */ + public function isAuthentificationSucceed(IRequest $request) : bool + { + $authentificationType = 'Basic'; + $authentification = $request->getHeaderLine('Authorization'); + if (0 !== stripos($authentification, $authentificationType)) { + throw new BadRequestException(); + } + + $authentification = substr($authentification, strlen($authentificationType) + 1); + list($this->login, $password) = explode(':', base64_decode($authentification)); + + $utilisateur = $this->getRepository()->find([ + 'login' => $this->login, + 'isActif' => true, + ]); + + return $utilisateur->isPasswordMatching($password); + } + + /** + * {@inheritDoc} + */ + public function getLogin() : string + { + return $this->login; + } + + /** + * @var string Login de l'utilisateur en cours de connexion + */ + private $login = ''; +} diff --git a/Tools/Services/LdapAuthentifierService.php b/Tools/Services/LdapAuthentifierService.php new file mode 100644 index 00000000..06cc1d7f --- /dev/null +++ b/Tools/Services/LdapAuthentifierService.php @@ -0,0 +1,20 @@ +queryBuilder->select('*, u_login AS id'); // @TODO: supprimer cette ligne quand on passera à DBAL > 2.6 : https://github.com/doctrine/dbal/commit/e937f37a8acc117047ff4ed9aec493a1e3de2195 - $this->queryBuilder->resetQueryPart('from'); + $this->queryBuilder->resetQueryParts(); + $this->queryBuilder->select('*, u_login AS id'); $this->queryBuilder->from($this->getTableName(), 'current'); $this->setWhere($this->getParamsConsumer2Storage($parametres)); $res = $this->queryBuilder->execute(); @@ -125,16 +125,16 @@ final protected function getStorage2Entite(array $dataStorage) : array final protected function getParamsConsumer2Storage(array $paramsConsumer) : array { $results = []; - if (!empty($paramsConsumer['login'])) { + if (array_key_exists('login', $paramsConsumer)) { $results['u_login'] = (string) $paramsConsumer['login']; } - if (!empty($paramsConsumer['token'])) { + if (array_key_exists('token', $paramsConsumer)) { $results['token'] = (string) $paramsConsumer['token']; } - if (!empty($paramsConsumer['gt_date_last_access'])) { + if (array_key_exists('gt_date_last_access', $paramsConsumer)) { $results['gt_date_last_access'] = (string) $paramsConsumer['gt_date_last_access']; } - if (!empty($paramsConsumer['isActif'])) { + if (array_key_exists('isActif', $paramsConsumer)) { $results['is_active'] = $paramsConsumer['isActif']; } return $results; @@ -148,11 +148,11 @@ public function postOne(array $data) : int public function putOne($id, array $data) : AEntite { $entite = $this->getOne($id); + // @TODO: supprimer cette ligne quand on passera à DBAL > 2.6 : https://github.com/doctrine/dbal/commit/e937f37a8acc117047ff4ed9aec493a1e3de2195 + $this->queryBuilder->resetQueryParts(); $entite->populate($data); $this->queryBuilder->update($this->getTableName()); $this->setSet($this->getEntite2Storage($entite)); - // @TODO: supprimer cette ligne quand on passera à DBAL > 2.6 : https://github.com/doctrine/dbal/commit/e937f37a8acc117047ff4ed9aec493a1e3de2195 - $this->queryBuilder->resetQueryPart('where'); $this->setWhere(['u_login' => $entite->getId()]); $this->queryBuilder->execute(); @@ -186,19 +186,19 @@ final protected function setSet(array $parametres) */ final protected function setWhere(array $parametres) { - if (!empty($parametres['u_login'])) { + if (array_key_exists('u_login', $parametres)) { $this->queryBuilder->andWhere('u_login = :id'); $this->queryBuilder->setParameter(':id', $parametres['u_login']); } - if (!empty($parametres['token'])) { + if (array_key_exists('token', $parametres)) { $this->queryBuilder->andWhere('token = :token'); $this->queryBuilder->setParameter(':token', $parametres['token']); } - if (!empty($parametres['gt_date_last_access'])) { + if (array_key_exists('gt_date_last_access', $parametres)) { $this->queryBuilder->andWhere('date_last_access >= :gt_date_last_access'); $this->queryBuilder->setParameter(':gt_date_last_access', $parametres['gt_date_last_access']); } - if (!empty($parametres['is_active'])) { + if (array_key_exists('is_active', $parametres)) { $this->queryBuilder->andWhere('u_is_active = :actif'); $this->queryBuilder->setParameter(':actif', ($parametres['is_active']) ? 'Y' : 'N'); } @@ -260,10 +260,10 @@ public function regenerateToken(AEntite $entite) $entite->updateDateLastAccess(); $entite->populateToken($this->buildToken($instanceToken)); + // @TODO: supprimer cette ligne quand on passera à DBAL > 2.6 : https://github.com/doctrine/dbal/commit/e937f37a8acc117047ff4ed9aec493a1e3de2195 + $this->queryBuilder->resetQueryParts(); $this->queryBuilder->update($this->getTableName()); $this->setSet($this->getEntite2Storage($entite)); - // @TODO: supprimer cette ligne quand on passera à DBAL > 2.6 : https://github.com/doctrine/dbal/commit/e937f37a8acc117047ff4ed9aec493a1e3de2195 - $this->queryBuilder->resetQueryPart('where'); $this->setWhere(['u_login' => $entite->getId()]); $this->queryBuilder->execute(); diff --git a/configuration.json.example b/configuration.json.example index cd062a39..b5c86919 100644 --- a/configuration.json.example +++ b/configuration.json.example @@ -4,5 +4,22 @@ "base": "", "utilisateur": "", "mot_de_passe" : "" - } + }, + "ldap": { + "serveur":"", + "protocol":, + "up_serveur":"", + "base":"", + "utilisateur":"", + "mot_de_passe":"", + "domaine":"", + "prenom":"", + "nom":"", + "mail":"", + "login":"", + "nom_affiche":"", + "filtre":"", + "filtre_recherche":"" + }, + "stage" : "production" } diff --git a/di-config.php b/di-config.php index 0cdf586a..0597ecae 100644 --- a/di-config.php +++ b/di-config.php @@ -12,6 +12,7 @@ use LibertAPI\Tools\Controllers\AuthentificationController; use LibertAPI\Utilisateur\UtilisateurRepository; use LibertAPI\Tools\Libraries\Application; +use LibertAPI\Tools\Libraries\StorageConfiguration; use function DI\get; use function DI\create; use function DI\autowire; @@ -41,8 +42,16 @@ ->method('setCacheFile', get('settings.routerCacheFile')), Slim\Router::class => get('router'), 'callableResolver' => autowire(CallableResolver::class), - 'environment' => function () { - return new Slim\Http\Environment($_SERVER); + 'environment' => function (C $c) { + $configuration = json_decode(file_get_contents(ROOT_PATH . 'configuration.json'), true); + $stage = (!isset($configuration['stage']) || 'development' !== $configuration['stage']) + ? 'production' + : 'development'; + + $e = new Slim\Http\Environment($_SERVER); + $e->set('stage', $stage); + + return $e; }, 'request' => function (C $c) { return Request::createFromEnvironment($c->get('environment')); @@ -58,7 +67,7 @@ AuthentificationController::class => function (C $c) { $repo = $c->get(UtilisateurRepository::class); $repo->setApplication($c->get(Application::class)); - return new AuthentificationController($repo, $c->get(IRouter::class)); + return new AuthentificationController($repo, $c->get(IRouter::class), $c->get(StorageConfiguration::class)); }, IRouter::class => get('router'), 'badRequestHandler' => function (C $c) {