<?php
namespace App\Controller;
use App\Entity\OAuth2AutoRequest;
use App\Entity\User;
use App\Form\LoginType;
use App\Form\ResetPassType;
use App\Service\Api\Api;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface;
use Trikoder\Bundle\OAuth2Bundle\Model\Client as OAuth2Client;
use Trikoder\Bundle\OAuth2Bundle\Model\Grant;
use Trikoder\Bundle\OAuth2Bundle\Model\RedirectUri;
use Trikoder\Bundle\OAuth2Bundle\Model\Scope;
class AuthController extends UserPageController {
const PAGE_TITLE = 'page.title.login';
protected $page;
protected $user;
protected $em;
protected $tokenGenerator;
protected $translator;
protected $api;
public function __construct(Security $security, EntityManagerInterface $em, Api $api, TranslatorInterface $translator, TokenGeneratorInterface $tokenGenerator){
$this->user = $security->getUser();
$this->em = $em;
$this->translator = $translator;
$this->tokenGenerator = $tokenGenerator;
$this->api = $api;
}
public function resetPass(Request $request, TranslatorInterface $translator) : Response
{
$resetForm = $this->createForm(ResetPassType::class);
$resetForm->handleRequest($request);
if ($resetForm->isSubmitted() && $resetForm->isValid()) {
$reset = $resetForm->getData();
$username = $reset['username'];
$uRepo = $this->em->getRepository(User::class);
if($user = $uRepo->findOneBy(['username' => $username])){
$pass = $this->api->users()->resetPass($user, substr($this->tokenGenerator->generateToken(), 0, 12));
$body = '<h2>'.$translator->trans('Password reset').'</h2>
<p>'.$translator->trans('Hello').' '.$user->getCompany().',</p>
<p>'.$translator->trans('A password reset was requested').'</p>
<p>'.$translator->trans('Your new password is').'</p>
<p style="margin-top: 10px;"><b>'.$pass.'</b></p>
';
if($this->api->system()->sendEmailUser($user, 'Password reset', $body)){
//$this->addFlash('success', 'A new password was sent to your email address');
return new JsonResponse([
'success' => true
], 200);
}
} else {
//$resetForm->addError(new FormError('User not found'));
return new JsonResponse([
'success' => false,
'error' => [
'status' => 404,
'message' => 'User not found'
]
], 404);
}
}
return new JsonResponse([
'success' => false,
'error' => [
'status' => 400,
'message' => 'Error'
]
], 400);
}
public function login(Request $request, AuthenticationUtils $authenticationUtils, TranslatorInterface $translator) : Response
{
if($this->get('security.authorization_checker')->isGranted('ROLE_USER')){
$acceptsJsonContent = in_array('application/json', $request->getAcceptableContentTypes());
if($acceptsJsonContent){
return new JsonResponse([
'success' => true
]);
} else {
return new RedirectResponse($this->generateUrl('utmailer_dashboard'));
}
}
$loginForm = $this->createForm(LoginType::class);
$loginForm->add('login', SubmitType::class, [
'label' => 'Sign in'
]);
$resetForm = $this->createForm(ResetPassType::class, null, ['action' => $this->generateUrl('utmailer_pass_reset')]);
$resetForm->add('reset', SubmitType::class, [
'label' => 'Send reset email'
]);
// get the login error if there is one
if ($error = $authenticationUtils->getLastAuthenticationError()) {
$this->addFlash('error', $translator->trans('security.'.$error->getMessageKey()));
}
return $this->render('login.html.twig', [
'page' => $this->page,
'loginform' => $loginForm->createView(),
'resetform' => $resetForm->createView()
]);
}
public function oauthConsent(Request $request, ClientManagerInterface $clientManager)
{
if($request->query->get('scopes')) {
$request_scopes = explode(' ', $request->query->get('scopes'));
} else {
$request_scopes = $clientManager->find($request->query->get('client_id'))->getScopes();
}
$return_url = $request->query->get('return_url');
$client_id = $request->query->get('client_id');
$scopes = array();
foreach ($request_scopes as $scope){
list($entity_name, $action) = explode('.', $scope);
$scopes[$entity_name][] = $action;
}
// TODO detect if autoauth?
$autoauth = false;
return $this->render('oauth-consent.html.twig', [
'page' => $this->page,
'scopes' => $scopes,
'client_id' => $client_id,
'return_url' => $return_url,
'autoauth' => $autoauth
]);
}
public function oauthAutoRequestStart(Request $request): JsonResponse
{
// Create API client and a token to retrieve it to be stored with the secret
$data = json_decode($request->getContent(), true);
$domain = $data['domain'];
//$redirect_url = $data['redirect_url'];
$secret = $data['secret'];
$oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
if ($oAuth2AutoRequest = $oRepo->findOneBy(['domain' => $domain])) {
if ($oAuth2AutoRequest->getComplete() === true) {
// Request already completed, delete and show error
$this->em->remove($oAuth2AutoRequest->getClient());
$this->em->remove($oAuth2AutoRequest);
$this->em->flush();
return $this->json([], 401);
}
// Request not completed, update secret and get token
$oAuth2AutoRequest->setSecret($secret);
$token = $oAuth2AutoRequest->getToken();
} else {
$oAuth2AutoRequest = new OAuth2AutoRequest();
$oAuth2AutoRequest->setDomain($domain);
$oAuth2AutoRequest->setSecret($secret);
$oAuth2AutoRequest->setCreatedAt(new \DateTimeImmutable());
$oAuth2AutoRequest->setComplete(false);
// Create token
$token = uniqid('api_');
$oAuth2AutoRequest->setToken($token);
// Create OAuth2 client
$clientId = bin2hex(random_bytes(16));
$clientSecret = bin2hex(random_bytes(32));
$oauth2Client = new OAuth2Client($clientId, $clientSecret);
$redirectUri = new RedirectUri($this->generateUrl('utmailer_oauth_auto_request_grant', ['token' => $oAuth2AutoRequest->getToken()], UrlGeneratorInterface::ABSOLUTE_URL));
//$redirectUri = new RedirectUri($redirect_url);
$oauth2Client->setRedirectUris($redirectUri);
$oauth2Client->setActive(true);
$grants = array_map(
static function (string $grant): Grant {
return new Grant($grant);
},
['credentials_grant', 'authorization_code']
);
$oauth2Client->setGrants(...$grants);
$scopesList = [
'profile.read',
'contacts.read',
'contacts.create',
'contacts.update',
'contacts.delete',
'contactgroups.read',
'contactgroups.create',
'contactgroups.update',
'contactgroups.delete'
];
$scopes = array_map(
static function (string $scope): Scope {
return new Scope($scope);
},
$scopesList
);
$oauth2Client->setScopes(...$scopes);
// set oauth2_client and persist
$oAuth2AutoRequest->setClient($oauth2Client);
$this->em->persist($oauth2Client);
$this->em->persist($oAuth2AutoRequest);
$this->em->flush();
}
return $this->json([
'token' => $token
], 200);
}
public function oauthAutoRequestOauth($token, Request $request)
{
$oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
/** @var OAuth2AutoRequest $oAuth2AutoRequest */
$oAuth2AutoRequest = $oRepo->findOneBy([
'token' => $token
]);
if ($oAuth2AutoRequest) {
$oAuth2Client = $oAuth2AutoRequest->getClient();
return $this->redirectToRoute('utmailer_oauth_consent', ['client_id' => $oAuth2Client->getIdentifier(), 'return_url' => $this->generateUrl('utmailer_oauth_auto_request_grant', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL)]);
} else {
// TODO Some error page?
die('Invalid request token');
}
}
public function oauthAutoRequestGrant($token, Request $request)
{
$oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
/** @var OAuth2AutoRequest $oAuth2AutoRequest */
if (!$oAuth2AutoRequest = $oRepo->findOneBy([
'token' => $token
])) {
// TODO Some error page?
die('Invalid request token');
}
if ($code = $request->get('code')) {
$oAuth2AutoRequest->setCode($code);
$oAuth2AutoRequest->setComplete(true);
$this->em->flush();
// Request approved by user
return $this->render('oauth-autorequest-approved.html.twig', [
'page' => $this->page
]);
} else {
// Request denied by user
return $this->render('oauth-autorequest-denied.html.twig', [
'page' => $this->page
]);
}
}
public function oauthAutoRequestStatus($token, Request $request)
{
$oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
/** @var OAuth2AutoRequest $oAuth2AutoRequest */
if (!$oAuth2AutoRequest = $oRepo->findOneBy([
'token' => $token
])){
return $this->json([], 404);
}
return $this->json([
'status' => ($oAuth2AutoRequest->getComplete() ? 'complete' : 'pending')
]);
}
public function oauthAutoRequestFinish($token, Request $request, ClientManagerInterface $clientManager)
{
$oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
/** @var OAuth2AutoRequest $oAuth2AutoRequest */
if (!$oAuth2AutoRequest = $oRepo->findOneBy([
'token' => $token
])){
return $this->json([], 404);
}
// Create API client and a token to retrieve it to be stored with the secret
$data = json_decode($request->getContent(), true);
$domain = $data['domain'];
$secret = $data['secret'];
if (!$oAuth2AutoRequest->getSecret() === $secret) {
return $this->json([], 403);
}
$httpClient = HttpClient::create();
try {
$params = [
'client_id' => $oAuth2AutoRequest->getClient()->getIdentifier(),
'client_secret' => $oAuth2AutoRequest->getClient()->getSecret(),
'code' => $oAuth2AutoRequest->getCode(),
'grant_type' => 'authorization_code'
];
$response = $httpClient->request('POST', $this->generateUrl('oauth2_token', [], UrlGeneratorInterface::ABSOLUTE_URL), [
'headers' => [
'Accept' => 'application/json',
//'Content-Type' => 'application/json',
],
'body' => $params
]);
$data = json_decode($response->getContent(), true);
$this->em->remove($oAuth2AutoRequest);
$this->em->flush();
return $this->json([
'access_token' => $data['access_token'],
'refresh_token' => $data['refresh_token'],
'expires_in' => $data['expires_in']
]);
} catch (\Exception $e) {
return $this->json($e->getMessage(), 400);
}
}
public function logout(){
}
}