src/Controller/AuthController.php line 99

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\OAuth2AutoRequest;
  4. use App\Entity\User;
  5. use App\Form\LoginType;
  6. use App\Form\ResetPassType;
  7. use App\Service\Api\Api;
  8. use Doctrine\ORM\EntityManagerInterface;
  9. use Symfony\Component\HttpClient\HttpClient;
  10. use Symfony\Component\HttpFoundation\JsonResponse;
  11. use Symfony\Component\HttpFoundation\RedirectResponse;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  14. use Symfony\Component\HttpFoundation\Response;
  15. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  16. use Symfony\Component\Security\Core\Security;
  17. use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
  18. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  19. use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
  20. use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
  21. use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
  22. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  23. use Symfony\Contracts\Translation\TranslatorInterface;
  24. use Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface;
  25. use Trikoder\Bundle\OAuth2Bundle\Model\Client as OAuth2Client;
  26. use Trikoder\Bundle\OAuth2Bundle\Model\Grant;
  27. use Trikoder\Bundle\OAuth2Bundle\Model\RedirectUri;
  28. use Trikoder\Bundle\OAuth2Bundle\Model\Scope;
  29. class AuthController extends UserPageController {
  30. const PAGE_TITLE = 'page.title.login';
  31. protected $page;
  32. protected $user;
  33. protected $em;
  34. protected $tokenGenerator;
  35. protected $translator;
  36. protected $api;
  37. public function __construct(Security $security, EntityManagerInterface $em, Api $api, TranslatorInterface $translator, TokenGeneratorInterface $tokenGenerator){
  38. $this->user = $security->getUser();
  39. $this->em = $em;
  40. $this->translator = $translator;
  41. $this->tokenGenerator = $tokenGenerator;
  42. $this->api = $api;
  43. }
  44. public function resetPass(Request $request, TranslatorInterface $translator) : Response
  45. {
  46. $resetForm = $this->createForm(ResetPassType::class);
  47. $resetForm->handleRequest($request);
  48. if ($resetForm->isSubmitted() && $resetForm->isValid()) {
  49. $reset = $resetForm->getData();
  50. $username = $reset['username'];
  51. $uRepo = $this->em->getRepository(User::class);
  52. if($user = $uRepo->findOneBy(['username' => $username])){
  53. $pass = $this->api->users()->resetPass($user, substr($this->tokenGenerator->generateToken(), 0, 12));
  54. $body = '<h2>'.$translator->trans('Password reset').'</h2>
  55. <p>'.$translator->trans('Hello').' '.$user->getCompany().',</p>
  56. <p>'.$translator->trans('A password reset was requested').'</p>
  57. <p>'.$translator->trans('Your new password is').'</p>
  58. <p style="margin-top: 10px;"><b>'.$pass.'</b></p>
  59. ';
  60. if($this->api->system()->sendEmailUser($user, 'Password reset', $body)){
  61. //$this->addFlash('success', 'A new password was sent to your email address');
  62. return new JsonResponse([
  63. 'success' => true
  64. ], 200);
  65. }
  66. } else {
  67. //$resetForm->addError(new FormError('User not found'));
  68. return new JsonResponse([
  69. 'success' => false,
  70. 'error' => [
  71. 'status' => 404,
  72. 'message' => 'User not found'
  73. ]
  74. ], 404);
  75. }
  76. }
  77. return new JsonResponse([
  78. 'success' => false,
  79. 'error' => [
  80. 'status' => 400,
  81. 'message' => 'Error'
  82. ]
  83. ], 400);
  84. }
  85. public function login(Request $request, AuthenticationUtils $authenticationUtils, TranslatorInterface $translator) : Response
  86. {
  87. if($this->get('security.authorization_checker')->isGranted('ROLE_USER')){
  88. $acceptsJsonContent = in_array('application/json', $request->getAcceptableContentTypes());
  89. if($acceptsJsonContent){
  90. return new JsonResponse([
  91. 'success' => true
  92. ]);
  93. } else {
  94. return new RedirectResponse($this->generateUrl('utmailer_dashboard'));
  95. }
  96. }
  97. $loginForm = $this->createForm(LoginType::class);
  98. $loginForm->add('login', SubmitType::class, [
  99. 'label' => 'Sign in'
  100. ]);
  101. $resetForm = $this->createForm(ResetPassType::class, null, ['action' => $this->generateUrl('utmailer_pass_reset')]);
  102. $resetForm->add('reset', SubmitType::class, [
  103. 'label' => 'Send reset email'
  104. ]);
  105. // get the login error if there is one
  106. if ($error = $authenticationUtils->getLastAuthenticationError()) {
  107. $this->addFlash('error', $translator->trans('security.'.$error->getMessageKey()));
  108. }
  109. return $this->render('login.html.twig', [
  110. 'page' => $this->page,
  111. 'loginform' => $loginForm->createView(),
  112. 'resetform' => $resetForm->createView()
  113. ]);
  114. }
  115. public function oauthConsent(Request $request, ClientManagerInterface $clientManager)
  116. {
  117. if($request->query->get('scopes')) {
  118. $request_scopes = explode(' ', $request->query->get('scopes'));
  119. } else {
  120. $request_scopes = $clientManager->find($request->query->get('client_id'))->getScopes();
  121. }
  122. $return_url = $request->query->get('return_url');
  123. $client_id = $request->query->get('client_id');
  124. $scopes = array();
  125. foreach ($request_scopes as $scope){
  126. list($entity_name, $action) = explode('.', $scope);
  127. $scopes[$entity_name][] = $action;
  128. }
  129. // TODO detect if autoauth?
  130. $autoauth = false;
  131. return $this->render('oauth-consent.html.twig', [
  132. 'page' => $this->page,
  133. 'scopes' => $scopes,
  134. 'client_id' => $client_id,
  135. 'return_url' => $return_url,
  136. 'autoauth' => $autoauth
  137. ]);
  138. }
  139. public function oauthAutoRequestStart(Request $request): JsonResponse
  140. {
  141. // Create API client and a token to retrieve it to be stored with the secret
  142. $data = json_decode($request->getContent(), true);
  143. $domain = $data['domain'];
  144. //$redirect_url = $data['redirect_url'];
  145. $secret = $data['secret'];
  146. $oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
  147. if ($oAuth2AutoRequest = $oRepo->findOneBy(['domain' => $domain])) {
  148. if ($oAuth2AutoRequest->getComplete() === true) {
  149. // Request already completed, delete and show error
  150. $this->em->remove($oAuth2AutoRequest->getClient());
  151. $this->em->remove($oAuth2AutoRequest);
  152. $this->em->flush();
  153. return $this->json([], 401);
  154. }
  155. // Request not completed, update secret and get token
  156. $oAuth2AutoRequest->setSecret($secret);
  157. $token = $oAuth2AutoRequest->getToken();
  158. } else {
  159. $oAuth2AutoRequest = new OAuth2AutoRequest();
  160. $oAuth2AutoRequest->setDomain($domain);
  161. $oAuth2AutoRequest->setSecret($secret);
  162. $oAuth2AutoRequest->setCreatedAt(new \DateTimeImmutable());
  163. $oAuth2AutoRequest->setComplete(false);
  164. // Create token
  165. $token = uniqid('api_');
  166. $oAuth2AutoRequest->setToken($token);
  167. // Create OAuth2 client
  168. $clientId = bin2hex(random_bytes(16));
  169. $clientSecret = bin2hex(random_bytes(32));
  170. $oauth2Client = new OAuth2Client($clientId, $clientSecret);
  171. $redirectUri = new RedirectUri($this->generateUrl('utmailer_oauth_auto_request_grant', ['token' => $oAuth2AutoRequest->getToken()], UrlGeneratorInterface::ABSOLUTE_URL));
  172. //$redirectUri = new RedirectUri($redirect_url);
  173. $oauth2Client->setRedirectUris($redirectUri);
  174. $oauth2Client->setActive(true);
  175. $grants = array_map(
  176. static function (string $grant): Grant {
  177. return new Grant($grant);
  178. },
  179. ['credentials_grant', 'authorization_code']
  180. );
  181. $oauth2Client->setGrants(...$grants);
  182. $scopesList = [
  183. 'profile.read',
  184. 'contacts.read',
  185. 'contacts.create',
  186. 'contacts.update',
  187. 'contacts.delete',
  188. 'contactgroups.read',
  189. 'contactgroups.create',
  190. 'contactgroups.update',
  191. 'contactgroups.delete'
  192. ];
  193. $scopes = array_map(
  194. static function (string $scope): Scope {
  195. return new Scope($scope);
  196. },
  197. $scopesList
  198. );
  199. $oauth2Client->setScopes(...$scopes);
  200. // set oauth2_client and persist
  201. $oAuth2AutoRequest->setClient($oauth2Client);
  202. $this->em->persist($oauth2Client);
  203. $this->em->persist($oAuth2AutoRequest);
  204. $this->em->flush();
  205. }
  206. return $this->json([
  207. 'token' => $token
  208. ], 200);
  209. }
  210. public function oauthAutoRequestOauth($token, Request $request)
  211. {
  212. $oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
  213. /** @var OAuth2AutoRequest $oAuth2AutoRequest */
  214. $oAuth2AutoRequest = $oRepo->findOneBy([
  215. 'token' => $token
  216. ]);
  217. if ($oAuth2AutoRequest) {
  218. $oAuth2Client = $oAuth2AutoRequest->getClient();
  219. return $this->redirectToRoute('utmailer_oauth_consent', ['client_id' => $oAuth2Client->getIdentifier(), 'return_url' => $this->generateUrl('utmailer_oauth_auto_request_grant', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL)]);
  220. } else {
  221. // TODO Some error page?
  222. die('Invalid request token');
  223. }
  224. }
  225. public function oauthAutoRequestGrant($token, Request $request)
  226. {
  227. $oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
  228. /** @var OAuth2AutoRequest $oAuth2AutoRequest */
  229. if (!$oAuth2AutoRequest = $oRepo->findOneBy([
  230. 'token' => $token
  231. ])) {
  232. // TODO Some error page?
  233. die('Invalid request token');
  234. }
  235. if ($code = $request->get('code')) {
  236. $oAuth2AutoRequest->setCode($code);
  237. $oAuth2AutoRequest->setComplete(true);
  238. $this->em->flush();
  239. // Request approved by user
  240. return $this->render('oauth-autorequest-approved.html.twig', [
  241. 'page' => $this->page
  242. ]);
  243. } else {
  244. // Request denied by user
  245. return $this->render('oauth-autorequest-denied.html.twig', [
  246. 'page' => $this->page
  247. ]);
  248. }
  249. }
  250. public function oauthAutoRequestStatus($token, Request $request)
  251. {
  252. $oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
  253. /** @var OAuth2AutoRequest $oAuth2AutoRequest */
  254. if (!$oAuth2AutoRequest = $oRepo->findOneBy([
  255. 'token' => $token
  256. ])){
  257. return $this->json([], 404);
  258. }
  259. return $this->json([
  260. 'status' => ($oAuth2AutoRequest->getComplete() ? 'complete' : 'pending')
  261. ]);
  262. }
  263. public function oauthAutoRequestFinish($token, Request $request, ClientManagerInterface $clientManager)
  264. {
  265. $oRepo = $this->em->getRepository(OAuth2AutoRequest::class);
  266. /** @var OAuth2AutoRequest $oAuth2AutoRequest */
  267. if (!$oAuth2AutoRequest = $oRepo->findOneBy([
  268. 'token' => $token
  269. ])){
  270. return $this->json([], 404);
  271. }
  272. // Create API client and a token to retrieve it to be stored with the secret
  273. $data = json_decode($request->getContent(), true);
  274. $domain = $data['domain'];
  275. $secret = $data['secret'];
  276. if (!$oAuth2AutoRequest->getSecret() === $secret) {
  277. return $this->json([], 403);
  278. }
  279. $httpClient = HttpClient::create();
  280. try {
  281. $params = [
  282. 'client_id' => $oAuth2AutoRequest->getClient()->getIdentifier(),
  283. 'client_secret' => $oAuth2AutoRequest->getClient()->getSecret(),
  284. 'code' => $oAuth2AutoRequest->getCode(),
  285. 'grant_type' => 'authorization_code'
  286. ];
  287. $response = $httpClient->request('POST', $this->generateUrl('oauth2_token', [], UrlGeneratorInterface::ABSOLUTE_URL), [
  288. 'headers' => [
  289. 'Accept' => 'application/json',
  290. //'Content-Type' => 'application/json',
  291. ],
  292. 'body' => $params
  293. ]);
  294. $data = json_decode($response->getContent(), true);
  295. $this->em->remove($oAuth2AutoRequest);
  296. $this->em->flush();
  297. return $this->json([
  298. 'access_token' => $data['access_token'],
  299. 'refresh_token' => $data['refresh_token'],
  300. 'expires_in' => $data['expires_in']
  301. ]);
  302. } catch (\Exception $e) {
  303. return $this->json($e->getMessage(), 400);
  304. }
  305. }
  306. public function logout(){
  307. }
  308. }