[USER][UI][AUTHENTICATION] Add registration form
This commit is contained in:
		| @@ -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()]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
							
								
								
									
										51
									
								
								src/Security/EmailVerifier.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/Security/EmailVerifier.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <?php | ||||
|  | ||||
| namespace App\Security; | ||||
|  | ||||
| use App\Core\DB\DB; | ||||
| use App\Core\Mailer; | ||||
| use Symfony\Bridge\Twig\Mime\TemplatedEmail; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| use Symfony\Component\Security\Core\User\UserInterface; | ||||
| use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface; | ||||
| use SymfonyCasts\Bundle\VerifyEmail\VerifyEmailHelperInterface; | ||||
|  | ||||
| class EmailVerifier | ||||
| { | ||||
|     private $verify_email_helper; | ||||
|  | ||||
|     public function __construct(VerifyEmailHelperInterface $helper) | ||||
|     { | ||||
|         $this->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(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										11
									
								
								templates/security/confirmation_email.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								templates/security/confirmation_email.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <h1>Hi! Please confirm your email!</h1> | ||||
|  | ||||
| <p> | ||||
|     Please confirm your email address by clicking the following link: <br><br> | ||||
|     <a href="{{ signedUrl|raw }}">Confirm my Email</a>. | ||||
|     This link will expire in {{ expiresAt|date('g') }} hour(s). | ||||
| </p> | ||||
|  | ||||
| <p> | ||||
|     Cheers! | ||||
| </p> | ||||
							
								
								
									
										16
									
								
								templates/security/register.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								templates/security/register.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| {% extends 'base.html.twig' %} | ||||
|  | ||||
| {% block title %}Register{% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
|     {% for flashError in app.flashes('verify_email_error') %} | ||||
|         <div class="alert alert-danger" role="alert">{{ flashError }}</div> | ||||
|     {% endfor %} | ||||
|  | ||||
|     <h1>Register</h1> | ||||
|  | ||||
|     <div style="position: relative; top: 10em; left: 20em;"> | ||||
|     {{ form(registration_form) }} | ||||
|     </div> | ||||
|  | ||||
| {% endblock %} | ||||
		Reference in New Issue
	
	Block a user