<?php
namespace Orioly\Controller\Login;
use Doctrine\ORM\ORMException;
use JMS\Serializer\SerializationContext;
use Nelmio\ApiDocBundle\Annotation\Operation;
use Orioly\Constants\Settings\OriolySettingsBundleConstants;
use Orioly\Entity\Login\User;
use Orioly\Entity\Login\UserToken;
use Orioly\Entity\Onboarding\AccountOnboardingOptions;
use Orioly\Entity\Onboarding\OnboardingStats;
use Orioly\Event\Login\UserEvent;
use Orioly\Exception\Services\FunctionalityNotAvailableException;
use Orioly\Form\Login\SecurityLoginFormType;
use Orioly\Services\Response\JsonResponse;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\User\UserInterface;
class SecurityController extends LoginBaseController
{
/**
* Login
*
* @Operation(
* tags={"Login"},
* consumes={"application/json"},
* produces={"application/json"},
* @SWG\Parameter(
* name="credentials",
* in="body",
* type="json",
* description="User Credentials",
* required=true,
* @SWG\Schema(
* type="object",
* @SWG\Property(property="username", type="string"),
* @SWG\Property(property="password", type="string")
* )
* ),
* @SWG\Response(
* response="200",
* description="Returned when successful"
* )
* )
*
* @param Request $request
* @return JsonResponse
* @throws \Exception
*/
public function loginAction(Request $request)
{
$form = $this->createForm(SecurityLoginFormType::class);
$form->submit($this->decodeJsonContent($request));
if ($form->isValid()) {
$user = $this->getUserManager()->findUserByUsernameOrEmail($form->getData()->getUsername());
if ($user->hasLastLogin()) {
$this->checkAndUpdateOnboardingOption($user);
}
$this->loginUser($user, $request);
if ($request->attributes->has('_mobile') && !$this->isGranted('CAN_USE_MOBILE_APPLICATION')) {
throw new FunctionalityNotAvailableException();
} else {
$response = $this->createResponse(
$this->serializeObject(
$user,
SerializationContext::create()->setGroups(['login_successful'])
)
);
}
} else {
$response = $this->createResponse(
$this->serializeObject($form),
Response::HTTP_BAD_REQUEST
);
}
return $response;
}
/**
* @param User|UserInterface $user
* @param Request $request
* @throws \Exception
*/
private function loginUser(User $user, Request $request)
{
$userManager = $this->getUserManager();
$createHashFromAccessingObject =
base64_encode(json_encode(
[
$request->headers->get('User-Agent'),
$request->getClientIp(),
]
));
$user->setTokenFromAccessingObject($createHashFromAccessingObject);
$user->setLastLogin(new \DateTime());
$userManager->updateUser($user);
$loginManager = $this->getSecurityLoginManager();
$loginManager->logInUser(
$user
);
$this->get('event_dispatcher')->dispatch(new UserEvent($user, $request), OriolySettingsBundleConstants::SECURITY_IMPLICIT_LOGIN);
}
/**
* @param Request $request
* @return JsonResponse
* @throws ORMException
*/
public function logoutAction(Request $request)
{
try {
$token = $request->headers->get(
$this->getParameter('api_key')
);
$user = $this->findUserByToken($token);
if (is_null($user)) {
throw new NotAcceptableHttpException();
}
$currentTokenList = ($user->getTokens()->filter(
function (UserToken $userToken) use ($token) {
return $userToken->getApiKey() == $token;
})
);
$currentToken = $currentTokenList->first();
$user->getTokens()->removeElement($currentToken);
$this->getEntityManager()->remove($currentToken);
$this->getUserManager()->updateUser($user);
$security = $this->getSecurityContext();
$token = new AnonymousToken($this->getParameter('secret'), new User());
$security->setToken($token);
$this->getSession()->invalidate();
$statusCode = Response::HTTP_OK;
} catch (NotAcceptableHttpException $exception) {
$statusCode = Response::HTTP_NOT_ACCEPTABLE;
}
return $this->createResponse('', $statusCode);
}
/**
* @param string $token
* @return User
* @throws \Doctrine\ORM\NonUniqueResultException
*/
private function findUserByToken($token)
{
return $this->getUserTokenRepository()->getUserFromToken($token);
}
/**
* @return JsonResponse
*/
public function retrieveInfoAction()
{
return $this->createResponse(
$this->serializeObject(
$this->getUser(),
SerializationContext::create()->setGroups(['login_successful'])
),
Response::HTTP_OK
);
}
/**
* @param UserInterface $user
* @throws ORMException
*/
private function checkAndUpdateOnboardingOption(UserInterface $user): void
{
$entityManager = $this->getEntityManager();
$optionName = OnboardingStats::OPTION_FIRST_VISIT_TO_DASHBOARD;
$account = $user->getAccount();
$option = $entityManager->getRepository(AccountOnboardingOptions::class)->findOneBy(
[
'optionName' => $optionName,
'userAccount' => $account->getId()
]
);
if (!$option) {
$entityManager->persist(
(new AccountOnboardingOptions())
->setOptionName($optionName)
->setValue('Y')
->setUserAccount($account)
);
}
}
}