<?php
namespace App\Controller;
use App\Tartarus;
use App\Annotation\Auth;
use App\Component\HttpFoundation\Payload;
use App\Component\Invoker\FrontInvoker;
use App\Component\Nexmo;
use App\Component\NexmoVerifyError;
use App\Component\UserConfirmation;
use App\Entity;
use Exception;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class AccountController extends Controller
{
/**
* List all accounts for user
*
* @Route("/account", name="account_list", methods="GET")
* @Auth(required=true)
*/
public function listAction(FrontInvoker $fi, Tartarus\AuthInterface $auth)
{
// @todo fix serialization of roles array
//$accounts = $this->getRepository(Entity\Account::class)->listAccounts($auth->getUserId());
$rawAccounts = $fi->accountList($auth->getUserId());
$accounts = [];
foreach ($rawAccounts as $account) {
$role = $account['role'];
unset($account['role']);
$assigned = $account['assigned'];
unset($account['assigned']);
if (!isset($accounts[$account['id']])) {
$accounts[$account['id']] = array_merge($account, ['role' => []]);
}
$accounts[$account['id']]['role'][$role] = ['created' => $assigned];
}
return $this->jsonOkResponse(['account' => array_values($accounts)]);
//return $this->jsonOkResponse(['account' => $accounts], ['groups' => ['list'], ProxyAwareObjectNormalizer::RESOLVE_PROXY_CLASSES => [Entity\Role::class, Entity\Account::class]]);
}
/**
* Get zone
*
* @Route("/account/{id}", name="account_get", methods="GET")
* @Auth(required=true)
*/
public function accountGetAction(FrontInvoker $fi, Payload $payload, Tartarus\AuthInterface $auth)
{
$accountId = $payload->path->id;
$fi->privilegeCheck($auth->getUserId(), $accountId, 'owner');
$account = $this->getRepository(Entity\Account::class)->findById($accountId);
if (!($account instanceof Entity\Account)) {
return $this->jsonSingleErrorResponse('Account not found', [], Response::HTTP_NOT_FOUND);
}
$account->fetchTrafficData();
return $this->jsonOkResponse(['account' => $account], Entity\Account::SERIALIZATION_CONTEXT['details']);
}
/**
* Get zone
*
* @Route("/account/test/{id}", name="account_get_test", methods="GET")
*/
public function accountGetTestAction(int $id)
{
$account = $this->getRepository(Entity\Account::class)->findById($id);
if (!($account instanceof Entity\Account)) {
return $this->jsonSingleErrorResponse('Account not found', [], Response::HTTP_NOT_FOUND);
}
$account->fetchTrafficData();
return $this->jsonOkResponse(['account' => $account], Entity\Account::SERIALIZATION_CONTEXT['details']);
}
/**
* Update account
*
* @Route("/account/{id}", name="account_update", methods="PUT")
* @Auth(required=true)
*/
public function accountUpdateAction(FrontInvoker $fi, Payload $payload, Tartarus\Auth $auth, Entity\Account $account)
{
$fi->privilegeCheck($auth->getUserId(), $payload->path->id, 'owner');
$account->assign($payload->body);
$this->db()->persist($account);
$this->db()->flush();
$account = $this->getRepository(Entity\Account::class)->findById($account->getId());
if (!($account instanceof Entity\Account)) {
return $this->jsonSingleErrorResponse('Account not found', [], Response::HTTP_NOT_FOUND);
}
$account->fetchTrafficData();
return $this->jsonOkResponse(['account' => $account], Entity\Account::SERIALIZATION_CONTEXT['details']);
}
/**
* Start postponed phone verification
*
* @Route("/account/{id}/verify", name="account_verify_start", methods="GET")
* @Auth(required=true)
*/
public function verifyStart(Payload $payload, FrontInvoker $fi, Tartarus\Auth $auth)
{
$user = $auth->getUser();
$account = current($fi->accountGetById($user['id'], $payload->path->id));
if ((int)$account['verified']) {
return $this->jsonSingleErrorResponse('Account already verified');
}
if ((int)$account['owner_id'] !== (int)$user['id']) {
return $this->jsonSingleErrorResponse('Only owner is allowed to verify account');
}
/* Initiate Nexmo validation */
$nexmo = new Nexmo();
try {
$requestId = $nexmo->verify($user['phone_number'], $user['email'], $next);
return $this->jsonOkResponse([
'call' => ($next ? 'next' : 'first'),
'request_id' => UserConfirmation::encryptRequestId($requestId, $account['id'] . $account['name'] . $user['email']),
'user' => ($auth->getUserPublic() + ['phone_number' => $user['phone_number']])
]
);
} catch (NexmoVerifyError $exception) {
return $this->jsonSingleErrorResponse($exception->getMessage());
} catch (Exception $exception) {
$this->storeException($exception);
return $this->jsonSingleErrorResponse('An error occurred during phone verification attempt');
}
}
/**
* Finalize postponed phone verification
*
* @Route("/account/{id}/verify", name="account_verify_complete", methods="POST")
* @Auth(required=true)
*/
public function verifyComplete(Payload $payload, FrontInvoker $fi, Tartarus\Auth $auth)
{
$user = $auth->getUser();
$accountId = $payload->path->id;
$errors = [];
$data = json_decode($payload->body, true);
if (!is_array($data)) {
return $this->jsonSingleErrorResponse('Malformed request body');
}
$account = current($fi->accountGetById($user['id'], intval($accountId)));
$originalRequestId = $data['request_id'];
try {
$data['request_id'] = UserConfirmation::decryptRequestId($data['request_id'], $account['id'] . $account['name'] . $user['email']);
} catch (Exception $e) {
$this->storeException($e);
$errors[] = ['fields' => ['request_id'], 'message' => $e->getMessage()];
}
/* Initiate Nexmo validation */
$nexmo = new Nexmo();
try {
$requestId = $nexmo->check($data['request_id'], (int)$data['code']);
$presult = current($fi->accountVerify(
(int)$user['id'],
(int)$accountId
));
return $this->jsonOkResponse(['account' => $presult]);
} catch (NexmoVerifyError $e) {
return $this->jsonSingleErrorResponse($e->getMessage(), ['request_id' => $originalRequestId, 'user' => ['phone_number' => $data['phone_number']]]); // @todo before was $pdata['phone_number'], no such var, what should be here?
} catch (Exception $e) {
$this->storeException($e);
return $this->jsonSingleErrorResponse('An error occurred during user confirmation');
}
}
}