From c973517397d69285dcdcdb228852b24949e09721 Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Sat, 25 Jul 2020 02:06:55 +0000 Subject: [PATCH] [USER][UI][AUTHENTICATION] Add registration form --- src/Controller/Security.php | 93 +++++++++++++++++++ src/Routes/Main.php | 1 + src/Security/EmailVerifier.php | 51 ++++++++++ .../security/confirmation_email.html.twig | 11 +++ templates/security/register.html.twig | 16 ++++ 5 files changed, 172 insertions(+) create mode 100644 src/Security/EmailVerifier.php create mode 100644 templates/security/confirmation_email.html.twig create mode 100644 templates/security/register.html.twig diff --git a/src/Controller/Security.php b/src/Controller/Security.php index 864556eb69..198eb2b85b 100644 --- a/src/Controller/Security.php +++ b/src/Controller/Security.php @@ -3,7 +3,24 @@ namespace App\Controller; use App\Core\Controller; +use App\Core\DB\DB; +use App\Core\Form; +use function App\Core\I18n\_m; +use App\Entity\LocalUser; +use App\Entity\Profile; +use App\Security\Authenticator; +use App\Security\EmailVerifier; +use app\Util\Common; +use App\Util\Nickname; +use Symfony\Component\Form\Extension\Core\Type\EmailType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; class Security extends Controller { @@ -25,4 +42,80 @@ class Security extends Controller { throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); } + + public function register(Request $request, + EmailVerifier $email_verifier, + GuardAuthenticatorHandler $guard_handler, + Authenticator $authenticator) + { + $form = Form::create([ + ['nickname', TextType::class, [ + 'label' => _m('Nickname'), + 'constraints' => [new Length([ + 'min' => 1, + 'minMessage' => _m('Your password should be at least {{ limit }} characters long'), + 'max' => 64, + 'maxMessage' => _m('Your password should be at most {{ limit }} characters long'), ]), + ], + ]], + ['email', EmailType::class, ['label' => _m('Email')]], + ['password', PasswordType::class, [ + 'label' => _m('Password'), + 'mapped' => false, + 'constraints' => [ + new NotBlank(['message' => _m('Please enter a password')]), + new Length(['min' => 6, 'minMessage' => _m('Your password should be at least {{ limit }} characters'), 'max' => 60]), + ], + ]], + ['register', SubmitType::class, ['label' => _m('Register')]], + ]); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $data = $form->getData(); + $data['password'] = $form->get('password')->getData(); + + $valid_nickname = Nickname::isValid($data['nickname'], Nickname::CHECK_USED); + if (!$valid_nickname) { + throw new \Exception(_m('Invalid nickname')); + } + + $profile = new Profile($data['nickname']); + $user = new LocalUser($data['nickname'], $data['email'], $data['password']); + + DB::persist($user); + DB::persist($profile); + DB::flush(); + + // generate a signed url and email it to the user + if (Common::config('site', 'use_email')) { + $email_verifier->sendEmailConfirmation( + 'verify_email', + $user, + (new TemplatedEmail()) + ->from(new Address(Common::config('site', 'email'), Common::config('site', 'nickname'))) + ->to($user->getOutgoingEmail()) + ->subject(_m('Please Confirm your Email')) + ->htmlTemplate('security/confirmation_email.html.twig') + ); + } else { + $user->setIsEmailVerified(true); + } + + return $guard_handler->authenticateUserAndHandleSuccess( + $user, + $request, + $authenticator, + 'main' // firewall name in security.yaml + ); + } + + return [ + '_template' => 'security/register.html.twig', + 'registration_form' => $form->createView(), + ]; + + return ['_template' => 'security/register.html.twig', 'form' => $form->createView()]; + } } diff --git a/src/Routes/Main.php b/src/Routes/Main.php index 602088fa94..f498e865e5 100644 --- a/src/Routes/Main.php +++ b/src/Routes/Main.php @@ -44,6 +44,7 @@ abstract class Main { $r->connect('login', '/login', [C\Security::class, 'login']); $r->connect('logout', '/logout', [C\Security::class, 'logout']); + $r->connect('register', '/register', [C\Security::class, 'register']); $r->connect('main_all', '/main/all', C\NetworkPublic::class); diff --git a/src/Security/EmailVerifier.php b/src/Security/EmailVerifier.php new file mode 100644 index 0000000000..fb77bc3a3a --- /dev/null +++ b/src/Security/EmailVerifier.php @@ -0,0 +1,51 @@ +verifyEmailHelper = $helper; + } + + public function sendEmailConfirmation(string $verify_email_route_name, UserInterface $user, TemplatedEmail $email): void + { + $signatureComponents = $this->verify_email_helper->generateSignature( + $verify_email_route_name, + $user->getId(), + $user->getOutgoingEmail() + ); + + $context = $email->getContext(); + $context['signedUrl'] = $signatureComponents->getSignedUrl(); + $context['expiresAt'] = $signatureComponents->getExpiresAt(); + + $email->context($context); + + Mailer::send($email); + } + + /** + * @throws VerifyEmailExceptionInterface + */ + public function handleEmailConfirmation(Request $request, UserInterface $user): void + { + $this->verify_email_helper->validateEmailConfirmation($request->getUri(), $user->getId(), $user->getOutgoingEmail()); + + $user->setIsEmailVerified(true); + + DB::persist($user); + DB::flush(); + } +} diff --git a/templates/security/confirmation_email.html.twig b/templates/security/confirmation_email.html.twig new file mode 100644 index 0000000000..2a6aede56a --- /dev/null +++ b/templates/security/confirmation_email.html.twig @@ -0,0 +1,11 @@ +

Hi! Please confirm your email!

+ +

+ Please confirm your email address by clicking the following link:

+ Confirm my Email. + This link will expire in {{ expiresAt|date('g') }} hour(s). +

+ +

+ Cheers! +

diff --git a/templates/security/register.html.twig b/templates/security/register.html.twig new file mode 100644 index 0000000000..5b30cb2656 --- /dev/null +++ b/templates/security/register.html.twig @@ -0,0 +1,16 @@ +{% extends 'base.html.twig' %} + +{% block title %}Register{% endblock %} + +{% block body %} + {% for flashError in app.flashes('verify_email_error') %} + + {% endfor %} + +

Register

+ +
+ {{ form(registration_form) }} +
+ +{% endblock %}