<?php
namespace App\Controller\Website\Mybiz\Member;
use App\Constants\AppConstant;
use App\Dto\Api\Member\MemberInformationsDto;
use App\Dto\Api\Member\MemberLoginDto;
use App\Dto\Api\User\UserJwtTokenDto;
use App\Dto\Authorization\AuthorizationHeaderDto;
use App\Dto\Member\MemberChangePasswordDto;
use App\Dto\Member\MemberCreatorDto;
use App\Dto\Member\MemberPersonalInformationDto;
use App\Entity\Member;
use App\Entity\Space;
use App\Entity\User;
use App\EventListener\Api\TokenInterceptor\MybizTokenAuthenticatorInterface;
use App\Exception\Country\CountryNotFoundException;
use App\Exception\Member\MemberNotFoundException;
use App\Exception\User\UserAlreadyExistsExcecption;
use App\Repository\MemberRepository;
use App\Repository\SpaceRepository;
use App\Repository\UserRepository;
use App\Service\Api\ApiRequestContentParser;
use App\Service\Api\Mobile\Login\MemberLoginResponseDtoProvider;
use App\Service\Authorization\MybizRequestAuthorizationChecker;
use App\Service\Authorization\MybizRequestJwtChecker;
use App\Service\Log\LogAdder;
use App\Service\Log\WebhookLogAdder;
use App\Service\Member\API\MemberResponseProvider;
use App\Service\Member\Jwt\UserJwtManager;
use App\Service\Member\MemberInformation\MemberInformationProvider;
use App\Service\Member\MemberPasswordUpdater;
use App\Service\Member\MemberUpdater;
use App\Service\Member\Subscription\MemberSignupCreator;
use App\Service\Notification\NotificationContentProvider;
use App\Service\Notification\NotificationSaver;
use App\Traits\JsonRequestTrait;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
class MemberApiController extends AbstractController implements MybizTokenAuthenticatorInterface
{
use JsonRequestTrait;
/**
* Création d'un membre
*
* Création d'un member depuis l'API.
*
* @Route("/v1/member/create", name="api_member_create", methods={"POST"})
*/
public function createMember(
Request $request,
TranslatorInterface $translator,
WebhookLogAdder $webhookLogAdder,
MybizRequestAuthorizationChecker $mybizRequestAuthorizationChecker,
ApiRequestContentParser $apiRequestContentParser,
MemberSignupCreator $memberSignupCreator,
LogAdder $logAdder,
NotificationContentProvider $notificationContentProvider,
NotificationSaver $notificationSaver,
SpaceRepository $spaceRepository
): JsonResponse
{
try {
$preferredLanguage = $request->headers->get("accept-language");
if (null === $preferredLanguage || false === in_array($preferredLanguage, AppConstant::LOCALES, true)) {
$preferredLanguage = AppConstant::DEFAULT_LOCALE;
}
/** @var MemberCreatorDto|null $memberCreatorDto */
$memberCreatorDto = $apiRequestContentParser->parseRequestContentForMember(
$this->getJsonFromContent($request),
$preferredLanguage
);
} catch (\JsonException|MemberNotFoundException $e) {
return $mybizRequestAuthorizationChecker->getParseErrorMessage($e);
}
$webhookLogAdder->add(
Space::SPACE_LUUME,
null,
null,
"Inscription du membre {$memberCreatorDto->getEmail()} depuis le endpoint d'API",
$memberCreatorDto->toArray()
);
$firstSubscriptionSpace = $spaceRepository->findOneBy(["name" => Space::SPACE_LUUME]);
if (null === $firstSubscriptionSpace) {
return $mybizRequestAuthorizationChecker->getDefaultErrorMessage(new \RuntimeException("firstSubscriptionSpace not found"));
}
try {
$member = $memberSignupCreator->createMember(
$request,
$memberCreatorDto,
$firstSubscriptionSpace
);
$memberSponsor = $member->getSponsor();
$sponsorId = null !== $memberSponsor ? $memberSponsor->getId() : null;
$sponsorName = null !== $memberSponsor ? $memberSponsor->getFullname() : null;
if ($member->isOfferedSponsor()) {
$notification = $notificationContentProvider->provide(
NotificationContentProvider::NOTIFICATION_OFFERED_SPONSOR,
// On spécifie le membre et non le sponsor pour passer dans le mail des informations du membre
// ensuite récupérer le sponsor en faisant $member->getSponsor().
$member
);
// On envoie aussi un mail
$notification->setHasToSentByMail(true);
$notification->setIcon("lni lni-box-gift-1");
$notificationSaver->save($notification, true);
// Si on a offert le membre à un sponsor on ne renvoie pas l'information à Luume
$sponsorId = null;
$sponsorName = null;
}
/** @var User|null $user */
$user = $member->getUser();
$logAdder->add(
LogAdder::LOG_TYPE_MEMBER_CREATION,
null,
$member->getId(),
$user->getId(),
LogAdder::STATUS_OK,
"Création du membre {$member->getId()} depuis {$request->headers->get("App-Provider")} - {$request->headers->get("App-Version")}",
);
return $this->json([
"memberId" => $member->getId(),
"sponsorId" => $sponsorId,
"sponsorName" => $sponsorName
], Response::HTTP_OK);
} catch (UserAlreadyExistsExcecption $e) {
return $this->json([
"error" => $translator->trans("validator.user.email_already_exists", ["%email%" => $memberCreatorDto->getEmail()], "validator", $request->headers->get("App-Language"))
], Response::HTTP_CONFLICT);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getDefaultErrorMessage($e);
}
}
/**
* Tentative de connexion d'un membre par login mot de passe
*
* tentative de connexion d'un membre depuis l'API
*
* @Route("/v1/member/login", name="api_member_login", methods={"POST"})
*/
public function login(
Request $request,
EntityManagerInterface $em,
TranslatorInterface $translator,
ApiRequestContentParser $apiRequestContentParser,
UserJwtManager $userJwtManager,
MemberLoginResponseDtoProvider $memberLoginResponseDtoProvider,
MemberResponseProvider $memberResponseProvider,
MybizRequestAuthorizationChecker $mybizRequestAuthorizationChecker,
UserPasswordHasherInterface $passwordHasher,
UserRepository $userRepository
): JsonResponse
{
try {
$authorizationHeaderDto = AuthorizationHeaderDto::generateFromRequest($request);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getDefaultErrorMessage($e);
}
try {
/** @var MemberLoginDto|null $memberLoginDto */
$memberLoginDto = $apiRequestContentParser->parseRequestContent($request, ApiRequestContentParser::MEMBER_LOGIN_PARSER);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getParseErrorMessage($e);
}
/** @var User|null $user */
$user = $userRepository->findOneBy([
"email" => $memberLoginDto->getEmail()
]);
if (null === $user) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
if (false === $passwordHasher->isPasswordValid($user, $memberLoginDto->getPassword())) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
$member = $user->getMember();
if (null === $member) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
if (true === $member->isDeleted()) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
// création du token JWT
$userJwtManager->create($user);
$user->setLastLoginAt(new \DateTime());
$em->flush();
return $memberResponseProvider->getMemberLoginResponse($user, $member, $authorizationHeaderDto, $memberLoginResponseDtoProvider, $translator);
}
/**
* Tentative de connexion d'un membre par login JWT TOKEN
*
* tentative de connexion d'un membre depuis l'API
*
* @Route("/v1/member/jwt-auth", name="api_member_jwt_token", methods={"POST"})
*/
public function jwtTokenAuthentication(
Request $request,
TranslatorInterface $translator,
MybizRequestAuthorizationChecker $mybizRequestAuthorizationChecker,
ApiRequestContentParser $apiRequestContentParser,
UserJwtManager $userJwtManager,
MemberLoginResponseDtoProvider $memberLoginResponseDtoProvider,
MemberResponseProvider $memberResponseProvider,
MemberRepository $memberRepository
): JsonResponse
{
try {
$authorizationHeaderDto = AuthorizationHeaderDto::generateFromRequest($request);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getDefaultErrorMessage($e);
}
try {
/** @var UserJwtTokenDto|null $userJwtTokenDto */
$userJwtTokenDto = $apiRequestContentParser->parseRequestContent($request, ApiRequestContentParser::MEMBER_JWT_AUTHENTICATION_PARSER);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getParseErrorMessage($e);
}
/** @var Member|null $member */
$user = $userJwtManager->getUserByJwtToken($userJwtTokenDto->getJwtToken());
if (null === $user) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
if (false === $userJwtManager->isValidate($user)) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
$member = $user->getMember();
if (null === $member || true === $member->isDeleted()) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
return $memberResponseProvider->getMemberLoginResponse($user, $member, $authorizationHeaderDto, $memberLoginResponseDtoProvider, $translator);
}
/**
* Mise à jour d'un membre
*
* Mise à jour des informations d'un membre depuis l'API, pour modifier le mot de passe d'un membre, c'est le même endpoint qui est utilisé avec le mot de passe renseigné.
*
* @Route("/v1/member/update", name="api_member_update", methods={"POST"})
*/
public function updateMember(
Request $request,
TranslatorInterface $translator,
MybizRequestAuthorizationChecker $mybizRequestAuthorizationChecker,
WebhookLogAdder $webhookLogAdder,
ApiRequestContentParser $apiRequestContentParser,
MemberUpdater $memberUpdater,
MemberPasswordUpdater $memberPasswordUpdater,
MemberRepository $memberRepository
): JsonResponse
{
$appLanguage = $request->headers->get("App-Language");
try {
$requestContent = $this->getJsonFromContent($request);
/** @var MemberPersonalInformationDto|null $memberPersonalInformationDto */
$memberPersonalInformationDto = $apiRequestContentParser->parseRequestContent($request, ApiRequestContentParser::MEMBER_UPDATER_PARSER);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getParseErrorMessage($e);
}
$member = $memberRepository->findMemberByEmail($memberPersonalInformationDto->getEmail());
if (null === $member) {
return $this->json([
"error" => $translator->trans("validator.user.email_invalid", ["%email%" => $memberPersonalInformationDto->getEmail()], "validator", $appLanguage)
], Response::HTTP_CONFLICT);
}
$webhookLogAdder->add(
Space::SPACE_LUUME,
$member,
null,
"Mise à jour du membre {$memberPersonalInformationDto->getEmail()} depuis le endpoint d'API",
$memberPersonalInformationDto->toArray()
);
try {
$memberUpdater->updateMemberFromPersonalInformationDto($member, $memberPersonalInformationDto);
} catch (CountryNotFoundException $e) {
return $this->json([
"error" => $e->getMessage()
], Response::HTTP_NOT_FOUND);
}
$requestPassword = $requestContent["password"];
if (!empty($requestPassword)) {
try {
$memberPasswordUpdater->updatePassword($member, $requestPassword);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getDefaultErrorMessage($e);
}
}
return $this->json([], Response::HTTP_OK);
}
/**
* Mise à jour du mot de passe d'un membre
*
* @Route("/v1/member/update-password", name="api_member_update_password", methods={"POST"})
*/
public function updatePassword(
Request $request,
TranslatorInterface $translator,
UserPasswordHasherInterface $userPasswordHasher,
ApiRequestContentParser $apiRequestContentParser,
MemberPasswordUpdater $memberPasswordUpdater,
MybizRequestJwtChecker $mybizRequestJwtChecker
): JsonResponse
{
try {
$member = $mybizRequestJwtChecker->checkJwt($request);
} catch (\Throwable $e) {
return $mybizRequestJwtChecker->getUnknownJWTErrorMessage();
}
try {
/** @var MemberChangePasswordDto|null $memberChangePasswordDto */
$memberChangePasswordDto = $apiRequestContentParser->parseRequestContent($request, ApiRequestContentParser::MEMBER_UPDATE_PASSWORD_PARSER);
} catch (\Throwable $e) {
return $mybizRequestJwtChecker->getDefaultErrorMessage($e);
}
$user = $member->getUser();
if (false === $userPasswordHasher->isPasswordValid($user, $memberChangePasswordDto->old)) {
return $this->json([
"error" => $translator->trans("validator.password.old_mismatch", [], "validator")
], Response::HTTP_FORBIDDEN);
}
try {
$memberPasswordUpdater->updatePassword($member, $memberChangePasswordDto->new);
return $this->json([], Response::HTTP_OK);
} catch (\Throwable $e) {
$this->addFlash("danger", $translator->trans("validator.server_error", [], "validator"));
return $this->json([
"error" => $translator->trans("validator.password.update_error", [], "validator")
], Response::HTTP_OK);
}
}
/**
* Récupération des informations d'un membre
*
* Permet de récupérer certaines informations sur un membre
*
* @Route("/v1/member/informations", name="api_member_informations", methods={"POST"})
*/
public function getInformations(
Request $request,
TranslatorInterface $translator,
ApiRequestContentParser $apiRequestContentParser,
MybizRequestAuthorizationChecker $mybizRequestAuthorizationChecker,
MemberInformationProvider $memberInformationProvider,
UserRepository $userRepository
): JsonResponse
{
try {
$authorizationHeaderDto = AuthorizationHeaderDto::generateFromRequest($request);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getDefaultErrorMessage($e);
}
try {
/** @var MemberInformationsDto|null $memberInformationsDto */
$memberInformationsDto = $apiRequestContentParser->parseRequestContent($request, ApiRequestContentParser::MEMBER_INFORMATIONS_PARSER);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getParseErrorMessage($e);
}
/** @var User|null $user */
$user = $userRepository->findOneBy([
"email" => $memberInformationsDto->getEmail()
]);
if (null === $user) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
$member = $user->getMember();
if (null === $member) {
return $this->json([
"error" => $translator->trans("validator.user.id_invalid", [], "validator", $authorizationHeaderDto->getLanguage())
], Response::HTTP_FORBIDDEN);
}
return $this->json(
$memberInformationProvider->getMemberInformationResponseDto($member)->toArray(),
Response::HTTP_OK
);
}
/**
* Création d'un membre
*
* Création d'un member depuis l'API.
*
* @Route("/v1/member/check/{email}", name="api_member_check_email", methods={"GET"})
*/
public function checkUserEmail(
UserRepository $userRespository,
string $email
): JsonResponse
{
/** @var User|null $user */
$user = $userRespository->findOneBy([
"email" => $email
]);
if (null === $user) {
return $this->json([], Response::HTTP_NOT_FOUND);
}
return $this->json([], Response::HTTP_OK);
}
/**
* Route pour tester sir le JWT est encore valable ou non
*
* @Route("/v1/mobile/jwt/valid", name="api_mobile_jwt_valid", methods={"GET"})
*/
public function jwtValid(
Request $request,
MybizRequestAuthorizationChecker $mybizRequestAuthorizationChecker,
MybizRequestJwtChecker $mybizRequestJwtChecker,
MemberLoginResponseDtoProvider $memberLoginResponseDtoProvider,
MemberResponseProvider $memberResponseProvider,
TranslatorInterface $translator,
UserRepository $userRepository
): JsonResponse
{
try {
$authorizationHeaderDto = AuthorizationHeaderDto::generateFromRequest($request);
} catch (\Throwable $e) {
return $mybizRequestAuthorizationChecker->getDefaultErrorMessage($e);
}
try {
$member = $mybizRequestJwtChecker->checkJwt($request);
} catch (\Throwable $e) {
return $mybizRequestJwtChecker->getUnknownJWTErrorMessage();
}
/** @var User|null $user */
$user = $userRepository->findOneBy([
"email" => $member->getEmail()
]);
return $memberResponseProvider->getMemberLoginResponse($user, $member, $authorizationHeaderDto, $memberLoginResponseDtoProvider, $translator);
}
}