Skip to content
Snippets Groups Projects
Commit da0ea841 authored by Pierre Fleutot's avatar Pierre Fleutot
Browse files

Ajout d'un module de gestion des applis clientes

parent 98c4376c
No related branches found
No related tags found
No related merge requests found
......@@ -18,12 +18,12 @@ security:
api_token:
pattern: ^/api/token$
security: false
# api:
# pattern: ^/api(?!/doc$) # Accepts routes under /api except /api/doc (pour api/doc, on utilisera donc le firewall "main" ce qui permettra d'accéder au swagger quand on est authentifié via Session PHP avec le role Admin
# security: true
# stateless: true # Pas d'authentification, pas de session utilisateur (mais le compte user est vérifié via le token)
# oauth2: true
# user_checker: App\Security\EasyUserChecker
api:
pattern: ^/api(?!/doc$) # Accepts routes under /api except /api/doc (pour api/doc, on utilisera donc le firewall "main" ce qui permettra d'accéder au swagger quand on est authentifié via Session PHP avec le role Admin
security: true
stateless: true # Pas d'authentification, pas de session utilisateur (mais le compte user est vérifié via le token)
oauth2: true
user_checker: App\Security\EasyUserChecker
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
......
<?php
namespace App\Controller;
use App\Entity\Entry;
use App\Entity\Log;
use App\Entity\OAuth2ClientProfile;
use App\Form\ClientProfileType;
use App\Repository\OAuth2ClientProfileRepository;
use League\Bundle\OAuth2ServerBundle\Model\Client;
use Doctrine\Persistence\ManagerRegistry;
use League\Bundle\OAuth2ServerBundle\Repository\ClientRepository;
use League\Bundle\OAuth2ServerBundle\ValueObject\Grant;
use League\Bundle\OAuth2ServerBundle\ValueObject\RedirectUri;
use League\Bundle\OAuth2ServerBundle\ValueObject\Scope;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* Paramétrage des applications clientes autorisées
*
* @Route("/oauth-clients")
* @IsGranted("ROLE_ADMIN")
*/
class OauthClientController extends AbstractController
{
/**
* @Route("/", name="app_client_index", methods={"GET"})
*/
public function index(OAuth2ClientProfileRepository $oAuth2ClientProfileRepository): Response
{
return $this->render('oauth_client/index.html.twig', [
'clientProfiles' => $oAuth2ClientProfileRepository->findAll(),
]);
}
/**
* @Route("/créer", name="app_client_new", methods={"GET", "POST"})
*/
public function new(Request $request, ManagerRegistry $managerRegistry): Response
{
$clientProfile = new OAuth2ClientProfile();
$form = $this->createForm(ClientProfileType::class, $clientProfile);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$client = new Client($form->get('client_name')->getData(), $form->get('identifier')->getData(), $form->get('secret')->getData());
$client = $this->updateClient($client, $form);
$clientProfile->setClient($client);
$managerRegistry->getManager()->persist($clientProfile);
$managerRegistry->getManager()->flush();
return $this->redirectToRoute('app_client_index');
}
return $this->render('genericForm.html.twig', [
'title' => "Créer un client",
'form' => $form->createView(),
'back_url' => $this->generateUrl('app_client_index'),
]);
}
/**
* @Route("/{id}/modifier", name="app_client_edit", methods={"GET", "POST"})
*/
public function edit(Request $request, OAuth2ClientProfile $clientProfile, ManagerRegistry $managerRegistry): Response
{
$client = $clientProfile->getClient();
$form = $this->createForm(ClientProfileType::class, $clientProfile);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$client->setName($form->get('client_name')->getData());
$client = $this->updateClient($client, $form);
$managerRegistry->getManager()->flush();
return $this->redirectToRoute('app_client_index');
}
return $this->render('genericForm.html.twig', [
'title' => "Modifier un client",
'form' => $form->createView(),
'back_url' => $this->generateUrl('app_client_index'),
]);
}
private function updateClient(Client $client, FormInterface $form)
{
$uris = [];
foreach (array_map('trim', explode("\n", $form->get('redirectUris')->getData())) as $string) {
$redirectUri = new RedirectUri($string);
$uris[] = $redirectUri;
}
$client->setRedirectUris(...$uris);
$scopes = [];
foreach (array_map('trim', explode("\n", $form->get('scopes')->getData())) as $string) {
$scope = new Scope($string);
$scopes[] = $scope;
}
$client->setScopes(...$scopes);
$grants = [];
foreach (array_map('trim', explode("\n", $form->get('grants')->getData())) as $string) {
$grant = new Grant($string);
$grants[] = $grant;
}
$client->setGrants(...$grants);
$client->setActive($form->get('active')->getData());
return $client;
}
/**
* @Route("/{id}/supprimer", name="app_client_delete", methods={"POST"})
*/
public function delete(Request $request, OAuth2ClientProfile $clientProfile, OAuth2ClientProfileRepository $oAuth2ClientProfileRepository): Response
{
if ($this->isCsrfTokenValid('delete'.$clientProfile->getId(), $request->request->get('_token'))) {
$oAuth2ClientProfileRepository->remove($clientProfile, true);
}
return $this->redirectToRoute('app_client_index');
}
}
......@@ -64,7 +64,7 @@ class RegistrationController extends AbstractController
);
$this->sendRegistrationEmail($mailer, $user, $signatureComponents->getSignedUrl());
$this->addFlash('success', sprintf("Un email de vérification vous a été envoyé. Veuillez cliquer sur le lien inclus pour finaliser al création de votre compte avant de vous connecter."));
$this->addFlash('success', sprintf("Un email de vérification vous a été envoyé. Veuillez cliquer sur le lien inclus pour finaliser la création de votre compte avant de vous connecter."));
return $this->redirectToRoute('app_login');
}
......
<?php
namespace App\Form;
use App\Entity\GraphyList;
use App\Entity\Group;
use App\Entity\OAuth2ClientProfile;
use App\Entity\User;
use App\Languages\LanguagesIso;
use League\Bundle\OAuth2ServerBundle\ValueObject\RedirectUri;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ClientProfileType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$client = $builder->getData()->getClient();
$builder
->add('name', TextType::class, [
'label' => 'Profile Name',
])
->add('description', TextareaType::class, [
'label' => 'Profile Description',
])
->add('client_name', TextType::class, [
'mapped' => false,
'data' => $client ? $client->getName() : '',
]);
if (!$builder->getData()->getId()) {
$builder
->add('identifier', TextType::class, [
'mapped' => false,
])
->add('secret', TextType::class, [
'mapped' => false,
]);
}
$builder
->add('redirectUris', TextareaType::class, [
'mapped' => false,
'label' => 'redirect uris (separate items with new lines)',
'data' => $client ? implode("\n", $client->getRedirectUris()) : '',
])
->add('grants', TextareaType::class, [
'mapped' => false,
'label' => 'grants (separate items with new lines)',
'data' => $client ? implode("\n", $client->getGrants()) : '',
])
->add('scopes', TextareaType::class, [
'mapped' => false,
'label' => 'scopes (separate items with new lines)',
'data' => $client ? implode("\n", $client->getScopes()) : '',
])
->add('active', CheckboxType::class, [
'mapped' => false,
'data' => ($client && $client->isActive()) ? true : false,
])
->add('submit', SubmitType::class, [
'label' => 'Enregistrer',
])
;
// Ajout de contraintes
$builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
$form = $event->getForm();
foreach (array_map('trim', explode("\n", $form->get('redirectUris')->getData())) as $string) {
if (!filter_var($string, \FILTER_VALIDATE_URL)) {
$form->get('redirectUris')->addError(new FormError(sprintf('The \'%s\' string is not a valid URI.', $string)));
}
}
});
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => OAuth2ClientProfile::class,
]);
}
}
<?php
namespace App\Form;
use App\Entity\GraphyList;
use App\Entity\Group;
use App\Entity\OAuth2ClientProfile;
use App\Entity\User;
use App\Languages\LanguagesIso;
use League\Bundle\OAuth2ServerBundle\Model\Client;
use League\Bundle\OAuth2ServerBundle\ValueObject\RedirectUri;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ClientType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('identifier', null, [
'mapped' => false,
])
->add('name', null, [
'mapped' => false,
])
->add('secret', null, [
'mapped' => false,
])
->add('redirectUris', TextareaType::class, [
'mapped' => false,
'label' => 'redirect uris (separate items with new lines)',
])
->add('grants', TextareaType::class, [
'label' => 'grants (separate items with new lines)',
])
->add('scopes', TextareaType::class, [
'label' => 'scopes (separate items with new lines)',
])
->add('active')
;
$stringToArrayTransformer = new CallbackTransformer(
function ($listAsArray) {
// transform the array to a string
if ($listAsArray) {
return implode("\n", $listAsArray);
} else {
return '';
}
},
function ($listAsString) {
// transform the string back to an array
if (empty($listAsString)) {
return array();
} else {
return array_map('trim', explode("\n", $listAsString));
}
}
);
// $stringToRedirectUriArrayTransformer = new CallbackTransformer(
// function ($listAsArray) {
// // transform the array of redirectUris to a string
// if ($listAsArray) {
// return implode("\n", $listAsArray);
// } else {
// return '';
// }
// },
// function ($listAsString) {
// // transform the string back to an array of redirectUris
// if (empty($listAsString)) {
// return array();
// } else {
// $result = [];
// foreach (array_map('trim', explode("\n", $listAsString)) as $string) {
// $redirectUri = new RedirectUri($string);
// $result[] = $redirectUri;
// }
// return ...$result;
// }
// }
// );
$builder->get('scopes')->addModelTransformer($stringToArrayTransformer);
$builder->get('grants')->addModelTransformer($stringToArrayTransformer);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Client::class,
]);
}
}
......@@ -54,4 +54,4 @@
</div>
</div>
{% endblock %}
{% endblock %}
\ No newline at end of file
......@@ -18,10 +18,13 @@
{% if is_granted('ROLE_ADMIN') %}
<li class="nav-item">
<a class="nav-link {{ 'app_user_index' in app.request.attributes.get('_route') ? 'active' }}" href="{{ path('app_user_index') }}">Utilisateurs</a>
<a class="nav-link {{ 'app_user' in app.request.attributes.get('_route') ? 'active' }}" href="{{ path('app_user_index') }}">Utilisateurs</a>
</li>
<li class="nav-item">
<a class="nav-link {{ 'app_lexicon_index' in app.request.attributes.get('_route') ? 'active' }}" href="{{ path('app_lexicon_index') }}">Lexiques</a>
<a class="nav-link {{ 'app_lexicon' in app.request.attributes.get('_route') ? 'active' }}" href="{{ path('app_lexicon_index') }}">Lexiques</a>
</li>
<li class="nav-item">
<a class="nav-link {{ 'app_client' in app.request.attributes.get('_route') ? 'active' }}" href="{{ path('app_client_index') }}">Applis clientes</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ path('app.swagger_ui') }}">Swagger</a>
......
<form method="post" action="{{ path('app_client_delete', {'id': clientProfile.id}) }}" onsubmit="return confirm('Confirmer la suppression ?');">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ clientProfile.id) }}">
<button title="Supprimer" class="btn btn-xs btn-danger"><i class="bi-x-circle"></i></button>
</form>
{% extends 'base.html.twig' %}
{% block container %}container-fluid{% endblock %}
{% block title %}Clients{% endblock %}
{% block body %}
<div class="row justify-content-center m-5">
<div class="col-md-12">
<h3 class="card-title d-flex justify-content-between">
Clients
<a href="{{ path('app_client_new') }}" class="btn btn-dark"><i class="bi-plus"></i> Créer</a>
</h3>
<div class="card mt-3">
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Client profile name</th>
<th>Client profile description</th>
<th>Client Identifier</th>
<th>Client Name</th>
<th>Client Secret</th>
<th>Client Redirect Uris</th>
<th>Client Grants</th>
<th>Client Scopes</th>
<th>Client Active</th>
<th></th>
</tr>
</thead>
<tbody>
{% for clientProfile in clientProfiles %}
{% set client = clientProfile.client %}
<tr>
<td>{{ clientProfile.name }}</td>
<td>{{ clientProfile.description }}</td>
<td>{{ client.identifier }}</td>
<td>{{ client.name }}</td>
<td>{{ client.secret }}</td>
<td>{{ client.redirectUris|join('<br>')|raw }}</td>
<td>{{ client.grants|join('<br>')|raw }}</td>
<td>{{ client.scopes|join('<br>')|raw }}</td>
<td>{{ client.active ? 'Yes' : 'No' }}</td>
<td class="text-end">
<a href="{{ path('app_client_edit', {'id': clientProfile.id}) }}" class="btn btn-dark btn-xs">Modifier</a>
<div class="d-inline-block">{{ include('oauth_client/_delete_form.html.twig') }}</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="9">Aucun client</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment