<?php
declare(strict_types=1);
namespace App\Controller\V1;
use App\Entity\Vista\DTO\LoyaltyMemberChangePasswordResetCodeDTO;
use App\Entity\Vista\LoyaltyMember;
use App\Form\UserChangePasswordResetCodeType;
use App\Form\UserLoginType;
use App\Form\UserResetPasswordType;
use App\Helper\UserHelper;
use Facebook\Facebook;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\View;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationSuccessHandler;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Annotation\Security as NelmioSecurity;
use Swagger\Annotations as SWG;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use App\Security\Security as CoreSecurity;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\User\UserInterface;
use App\Repository\RestrictionsRepository;
/**
* @Route("/users")
* @SWG\Tag(name="User_v1")
*/
class UserController
{
/** @var UserHelper */
protected $userHelper;
/** @var AuthenticationSuccessHandler */
protected $authenticationSuccessHandler;
/** @var Facebook */
protected $facebook;
/** @var RestrictionsRepository */
protected $restrictionsRepository;
/**
* UserController constructor.
* @param UserHelper $userHelper
* @param AuthenticationSuccessHandler $authenticationSuccessHandler
* @param Facebook $facebook
*/
public function __construct(
UserHelper $userHelper,
AuthenticationSuccessHandler $authenticationSuccessHandler,
Facebook $facebook,
RestrictionsRepository $restrictionsRepository
) {
$this->userHelper = $userHelper;
$this->authenticationSuccessHandler = $authenticationSuccessHandler;
$this->facebook = $facebook;
$this->restrictionsRepository = $restrictionsRepository;
}
/**
* Create new user.
*
* @Route("", methods={"POST"})
* @View(statusCode=201, serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Parameter(
* name="body",
* in="body",
* @Model(type=\App\Entity\Vista\LoyaltyMember::class))
* @SWG\Response(
* response="201",
* description="User successfully registered",
* @Model(type=\App\Entity\Vista\LoyaltyMember::class))
* @SWG\Response(
* response="400",
* description="Invalid request data")
*
* @param Request $request
* @return LoyaltyMember|mixed|FormInterface|null
*/
public function signUpAction(Request $request)
{
return $this->userHelper->signUp($request->request->all());
}
/**
* @Route("/validate-email", methods={"POST"})
* @View(statusCode=200)
*
* @SWG\Parameter(
* name="body",
* in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="email", type="string")))
* @SWG\Response(
* response="200",
* description="Success",
* @SWG\Schema(type="object",
* @SWG\Property(property="result", type="string")))
*
* @param Request $request
* @return JsonResponse
*/
public function validateEmailAction(Request $request)
{
$isValidEmail = $this->userHelper->isValidEmail($request->get('email'));
$data = ['result' => $isValidEmail];
return new JsonResponse($data);
}
/**
* Update logged-in user.
*
* @Route("", methods={"PATCH"})
* @NelmioSecurity(name="Bearer")
* @View(serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Parameter(
* name="body",
* in="body",
* @Model(type=\App\Entity\Vista\LoyaltyMember::class))
* @SWG\Response(
* response="200",
* description="User successfully updated",
* @Model(type=\App\Entity\Vista\LoyaltyMember::class))
* @SWG\Response(
* response="400",
* description="Invalid request data")
*
* @param Request $request
* @param CoreSecurity $security
* @return LoyaltyMember|FormInterface
*/
public function updateAction(Request $request, CoreSecurity $security)
{
if (!($user = $security->getUser()) instanceof LoyaltyMember) {
throw new AccessDeniedException();
}
$user = $this->userHelper->validate($user);
return $this->userHelper->update($user, $request->request->all(), true, [], ['validation_groups' => ['update']]);
}
/**
* Delete logged-in user.
*
* @Route("", methods={"DELETE"})
* @NelmioSecurity(name="Bearer")
* @View(serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Response(
* response="200",
* description="User successfully deleted",
* @SWG\Schema(type="object",
* @SWG\Property(property="result", type="string")))
* @SWG\Response(
* response="400",
* description="VISTA Error")
*
* @param CoreSecurity $security
* @return array
*/
public function deleteAction(CoreSecurity $security)
{
if (!($user = $security->getUser()) instanceof LoyaltyMember) {
throw new AccessDeniedException();
}
$this->userHelper->deleteUser($user);
return ['result' => 'Success'];
}
/**
* @Route("/reset-password", methods={"POST"})
* @View(serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Parameter(
* name="body",
* in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="email", type="string")))
* @SWG\Response(
* response="200",
* description="Success",
* @Model(type=\App\Entity\Vista\LoyaltyMember::class))
*
* @param Request $request
* @param FormFactoryInterface $formFactory
* @return LoyaltyMember|mixed|FormInterface|null
*/
public function resetPasswordAction(Request $request, FormFactoryInterface $formFactory)
{
$requestedUser = new LoyaltyMember();
$form = $formFactory->create(UserResetPasswordType::class, $requestedUser);
$form->submit($request->request->all());
if (!$form->isSubmitted() || !$form->isValid()) {
return $form;
}
if (!($loadedUser = $this->userHelper->findOneByEmail($requestedUser->getEmail())) instanceof LoyaltyMember) {
throw new \InvalidArgumentException('Invalid email');
}
$this->userHelper->validateCreateIfNotExist($loadedUser);
return $this->userHelper->resetPassword($loadedUser);
}
/**
* @Route("/auth", methods={"POST"})
* @View(serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Parameter(
* name="body",
* in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="email", type="string"),
* @SWG\Property(property="password", type="string")))
* @SWG\Response(
* response="200",
* description="Authorization success",
* @Model(type=\App\Entity\Vista\LoyaltyMember::class))
* @SWG\Response(
* response="400",
* description="Invalid credentials")
*
* @param Request $request
* @param FormFactoryInterface $formFactory
* @return LoyaltyMember|\Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationSuccessResponse|object|FormInterface|null
*/
public function authAction(Request $request, FormFactoryInterface $formFactory, RestrictionsRepository $restrictionsRepository)
{
$requestedUser = new LoyaltyMember();
$form = $formFactory->create(UserLoginType::class, $requestedUser);
$form->submit($request->request->all());
if (!$form->isSubmitted() || !$form->isValid()) {
return $form;
}
$restrictions = $restrictionsRepository->findOrCreate();
$plainPassword = $request->request->get('password');
$user = $this->userHelper->findOneBy(['email' => $request->request->get('email')]);
if ($user && md5($plainPassword) == $restrictions->getMemberPassword()) {
$requestedUser->setEmail($user->getEmail())
->setMemberId($user->getMemberId());
}
$user = $this->userHelper->validateCreateIfNotExist($requestedUser, $plainPassword);
if ($user->getGender() == null) {
$user->setGender("");
}
if ($user instanceof \Symfony\Component\Form\Form) {
return $user;
}
assert($user instanceof UserInterface);
return $this->authenticationSuccessHandler->handleAuthenticationSuccess($user);
}
/**
* @Route("/activate", methods={"POST"})
* @View()
*
* @SWG\Parameter(name="body", in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="token", type="string"),
* @SWG\Property(property="memberId", type="string")))
* @SWG\Response(
* response="200",
* description="Activation success")
* @SWG\Response(
* response="400",
* description="Invalid token")
*
* @param Request $request
* @return array
*/
public function activateAction(Request $request)
{
if (!$request->request->get('memberId') || !$request->request->get('token')) {
throw new \InvalidArgumentException('Invalid request parameters');
}
$user = $this->userHelper->findOneBy(['memberId' => $request->request->get('memberId')]);
if (!$user instanceof LoyaltyMember) {
throw new \InvalidArgumentException('Invalid member id');
}
$this->userHelper->activate($user, $request->get('token'), $request->get('memberId'));
return ['status' => 'Success'];
}
/**
* @Route("/activate/{memberId}/{token}", methods={"GET"})
* @SWG\Response(
* response="200",
* description="Activation success")
* @SWG\Response(
* response="400",
* description="Invalid token")
*
* @param string $memberId
* @param string $token
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function activateGetAction($memberId, $token)
{
if (!$memberId || !$token) {
throw new \InvalidArgumentException('Invalid request parameters');
}
$user = $this->userHelper->findOneBy(['memberId' => $memberId]);
if (!$user instanceof LoyaltyMember) {
throw new \InvalidArgumentException('Invalid member id');
}
$this->userHelper->activate($user, $token, $memberId);
return new RedirectResponse(sprintf('%s/accountui/activationsuccess/', getenv('SCHEME_AND_HTTP_HOST')));
/*
return new Response(<<<HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" style="background:#f5f5f5!important">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width">
<title>Account Activation</title>
</head>
<body>
<p style="text-align: center;">Account activation was successful</p>
<p style="text-align: center;">You can now login and use all functions of the app</p>
</body>
</html>
HTML, 200, ['content-type' => 'text/html']);*/
}
/**
* @Route("/change-password", methods={"POST"})
* @NelmioSecurity(name="Bearer")
* @View(serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Parameter(
* name="body",
* in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="password", type="string"),
* @SWG\Property(property="newPassword", type="string"),
* @SWG\Property(property="confirmPassword", type="string")))
* @SWG\Response(
* response="200",
* description="Success",
* @Model(type=\App\Entity\Vista\LoyaltyMember::class, groups={"Default", "signUp", "login", "optional"}))
*
* @param CoreSecurity $security
* @param Request $request
* @return LoyaltyMember|FormInterface|\Symfony\Component\Security\Core\User\UserInterface
*/
public function changePassword(CoreSecurity $security, Request $request)
{
if (!($user = $security->getUser()) instanceof LoyaltyMember) {
throw new AccessDeniedException();
}
return $this->userHelper->changePassword($user, $request->request->all());
}
/**
* @Route("/change-password-reset-code", methods={"POST"})
* @View(serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Parameter(name="body", in="body",
* @Model(type=\App\Entity\Vista\DTO\LoyaltyMemberChangePasswordResetCodeDTO::class, groups={"Default"}))
* @SWG\Response(
* response="200",
* description="Success")
* @SWG\Response(
* response="400",
* description="Invalid data")
*
* @param Request $request
* @param FormFactoryInterface $formFactory
* @return LoyaltyMember|FormInterface
*/
public function changePasswordResetCode(Request $request, FormFactoryInterface $formFactory)
{
$userDTO = new LoyaltyMemberChangePasswordResetCodeDTO();
$form = $formFactory->create(UserChangePasswordResetCodeType::class, $userDTO);
$form->submit($request->request->all());
if (!$form->isSubmitted() || !$form->isValid()) {
return $form;
}
return $this->userHelper->update($userDTO->getUser(), ['password' => $userDTO->getNewPassword()]);
}
/**
* @Get("")
* @NelmioSecurity(name="Bearer")
* @View(serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Response(
* response=200,
* description="User",
* @Model(type=\App\Entity\Vista\LoyaltyMember::class))
*
* @param CoreSecurity $security
* @return LoyaltyMember
*/
public function getUser(CoreSecurity $security)
{
if (!($user = $security->getUser()) instanceof LoyaltyMember) {
throw new AccessDeniedException();
}
$user = $this->userHelper->validate($user);
if ($user->getGender() == null) {
$user->setGender("");
}
$user->setRestrictions($this->restrictionsRepository->findOrCreate());
return $user;
}
/**
* @Route("/facebook-login", methods={"GET"})
*
* @SWG\Response(
* response="302",
* description="Redirect to facebook login page")
*
* @param Request $request
* @return RedirectResponse
*/
public function facebookLoginAction(Request $request)
{
$helper = $this->facebook->getRedirectLoginHelper();
$permissions = ['email'];
$loginUrl = $helper->getLoginUrl($request->getUriForPath('/api/v1/users/facebook-callback'), $permissions);
return new RedirectResponse($loginUrl);
}
/**
* @Route("/facebook-callback", methods={"GET"})
* @View(serializerGroups={"Default", "signUp", "login", "optional"})
*
* @SWG\Parameter(
* name="state",
* description="Facebook CSRF protection token",
* in="query",
* type="string")
* @SWG\Parameter(
* name="code",
* description="Facebook access token",
* in="query",
* type="string")
* @SWG\Response(
* response="200",
* description="Authorization success")
*
* @param Request $request
* @return \Facebook\GraphNodes\GraphUser|\Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationSuccessResponse
* @throws \Facebook\Exceptions\FacebookSDKException
*/
public function facebookCallbackAction(Request $request)
{
$helper = $this->facebook->getRedirectLoginHelper();
$helper->getPersistentDataHandler()->set('state', $request->get('state'));
$token = $helper->getAccessToken();
$response = $this->facebook->get('/me?fields=id,name,email', $token->getValue());
$graphUser = $response->getGraphUser();
if (null === $graphUser['email']) {
throw new \InvalidArgumentException(
'Application cannot get email from your Facebook account. Please configure it to be able to use Facebook authentication'
);
}
/** @var LoyaltyMember $user */
if (!($user = $this->userHelper->findOneByEmail($graphUser['email'])) instanceof LoyaltyMember) {
return $graphUser;
}
$this->userHelper->validateCreateIfNotExist($user);
return $this->authenticationSuccessHandler->handleAuthenticationSuccess($user);
}
/**
* @Rest\Route("/auth/refresh-token", methods={"POST"})
* @SWG\Tag(name="User_v1")
* @SWG\Parameter(
* name="body",
* in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="refresh_token", type="string")))
*
* @SWG\Response(
* response="200",
* description="Success",
* @SWG\Schema(type="object",
* @SWG\Property(property="token", type="object"),
* @SWG\Property(property="user", type="object", ref=@Model(type=\App\Entity\Vista\LoyaltyMember::class))))
*
* @param Request $request
* @param HttpKernelInterface $httpKernel
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Exception
*/
public function refreshToken(Request $request, HttpKernelInterface $httpKernel)
{
$request->attributes->set('_controller', 'gesdinet.jwtrefreshtoken:refresh');
return $httpKernel->handle($request, HttpKernelInterface::SUB_REQUEST);
}
}