2020-06-14 20:15:53 +02:00
|
|
|
<?php namespace MapGuesser\Controller;
|
|
|
|
|
2020-06-28 03:18:51 +02:00
|
|
|
use DateTime;
|
|
|
|
use MapGuesser\Http\Request;
|
2020-06-14 21:04:20 +02:00
|
|
|
use MapGuesser\Interfaces\Authorization\ISecured;
|
2020-06-14 20:15:53 +02:00
|
|
|
use MapGuesser\Interfaces\Request\IRequest;
|
|
|
|
use MapGuesser\Interfaces\Response\IContent;
|
2020-06-28 03:18:51 +02:00
|
|
|
use MapGuesser\Interfaces\Response\IRedirect;
|
|
|
|
use MapGuesser\OAuth\GoogleOAuth;
|
2020-06-18 00:21:18 +02:00
|
|
|
use MapGuesser\PersistentData\PersistentDataManager;
|
|
|
|
use MapGuesser\PersistentData\Model\User;
|
2020-06-25 20:26:33 +02:00
|
|
|
use MapGuesser\Repository\UserConfirmationRepository;
|
2020-07-05 00:09:45 +02:00
|
|
|
use MapGuesser\Repository\UserPasswordResetterRepository;
|
2020-06-14 20:15:53 +02:00
|
|
|
use MapGuesser\Response\HtmlContent;
|
|
|
|
use MapGuesser\Response\JsonContent;
|
2020-06-28 03:18:51 +02:00
|
|
|
use MapGuesser\Response\Redirect;
|
|
|
|
use MapGuesser\Util\JwtParser;
|
2020-06-14 20:15:53 +02:00
|
|
|
|
2020-06-14 21:04:20 +02:00
|
|
|
class UserController implements ISecured
|
2020-06-14 20:15:53 +02:00
|
|
|
{
|
|
|
|
private IRequest $request;
|
|
|
|
|
2020-06-18 00:21:18 +02:00
|
|
|
private PersistentDataManager $pdm;
|
|
|
|
|
2020-06-25 20:26:33 +02:00
|
|
|
private UserConfirmationRepository $userConfirmationRepository;
|
|
|
|
|
2020-07-05 00:09:45 +02:00
|
|
|
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
|
|
|
|
2020-06-14 20:15:53 +02:00
|
|
|
public function __construct(IRequest $request)
|
|
|
|
{
|
|
|
|
$this->request = $request;
|
2020-06-18 00:21:18 +02:00
|
|
|
$this->pdm = new PersistentDataManager();
|
2020-06-25 20:26:33 +02:00
|
|
|
$this->userConfirmationRepository = new UserConfirmationRepository();
|
2020-07-05 00:09:45 +02:00
|
|
|
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
2020-06-14 20:15:53 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 21:04:20 +02:00
|
|
|
public function authorize(): bool
|
|
|
|
{
|
|
|
|
$user = $this->request->user();
|
|
|
|
|
|
|
|
return $user !== null;
|
|
|
|
}
|
|
|
|
|
2020-06-25 16:44:34 +02:00
|
|
|
public function getAccount(): IContent
|
2020-06-14 20:15:53 +02:00
|
|
|
{
|
2020-06-18 00:21:18 +02:00
|
|
|
/**
|
|
|
|
* @var User $user
|
|
|
|
*/
|
2020-06-14 20:15:53 +02:00
|
|
|
$user = $this->request->user();
|
|
|
|
|
2020-07-05 00:58:03 +02:00
|
|
|
return new HtmlContent('account/account', ['user' => $user->toArray()]);
|
2020-06-25 20:26:33 +02:00
|
|
|
}
|
|
|
|
|
2020-06-28 03:18:51 +02:00
|
|
|
public function getGoogleAuthenticateRedirect(): IRedirect
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var User $user
|
|
|
|
*/
|
|
|
|
$user = $this->request->user();
|
|
|
|
|
|
|
|
$state = bin2hex(random_bytes(16));
|
2020-07-05 12:25:25 +02:00
|
|
|
$nonce = bin2hex(random_bytes(16));
|
2020-06-28 03:18:51 +02:00
|
|
|
|
|
|
|
$this->request->session()->set('oauth_state', $state);
|
2020-07-05 12:25:25 +02:00
|
|
|
$this->request->session()->set('oauth_nonce', $nonce);
|
2020-06-28 03:18:51 +02:00
|
|
|
|
|
|
|
$oAuth = new GoogleOAuth(new Request());
|
|
|
|
|
|
|
|
$url = $oAuth->getDialogUrl(
|
|
|
|
$state,
|
|
|
|
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink(),
|
2020-07-05 12:25:25 +02:00
|
|
|
$nonce,
|
2020-06-28 03:18:51 +02:00
|
|
|
$user->getEmail()
|
|
|
|
);
|
|
|
|
|
|
|
|
return new Redirect($url, IRedirect::TEMPORARY);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function authenticateWithGoogle(): IContent
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var User $user
|
|
|
|
*/
|
|
|
|
$user = $this->request->user();
|
|
|
|
|
|
|
|
if ($this->request->query('state') !== $this->request->session()->get('oauth_state')) {
|
2020-07-05 00:58:03 +02:00
|
|
|
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
2020-06-28 03:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$oAuth = new GoogleOAuth(new Request());
|
2020-07-05 00:58:03 +02:00
|
|
|
$tokenData = $oAuth->getToken(
|
|
|
|
$this->request->query('code'),
|
|
|
|
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink()
|
|
|
|
);
|
2020-06-28 03:18:51 +02:00
|
|
|
|
|
|
|
if (!isset($tokenData['id_token'])) {
|
2020-07-05 00:58:03 +02:00
|
|
|
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
2020-06-28 03:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$jwtParser = new JwtParser($tokenData['id_token']);
|
2020-07-05 12:25:25 +02:00
|
|
|
$idToken = $jwtParser->getPayload();
|
2020-06-28 03:18:51 +02:00
|
|
|
|
2020-07-05 12:25:25 +02:00
|
|
|
if ($idToken['nonce'] !== $this->request->session()->get('oauth_nonce')) {
|
|
|
|
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($idToken['sub'] !== $user->getGoogleSub()) {
|
2020-07-05 00:58:03 +02:00
|
|
|
return new HtmlContent('account/google_authenticate', [
|
|
|
|
'success' => false,
|
|
|
|
'errorText' => 'This Google account is not linked to your account.'
|
|
|
|
]);
|
2020-06-28 03:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$authenticatedWithGoogleUntil = new DateTime('+45 seconds');
|
|
|
|
$this->request->session()->set('authenticated_with_google_until', $authenticatedWithGoogleUntil);
|
|
|
|
|
2020-07-05 00:58:03 +02:00
|
|
|
return new HtmlContent('account/google_authenticate', [
|
|
|
|
'success' => true,
|
|
|
|
'authenticatedWithGoogleUntil' => $authenticatedWithGoogleUntil
|
|
|
|
]);
|
2020-06-28 03:18:51 +02:00
|
|
|
}
|
|
|
|
|
2020-06-25 20:26:33 +02:00
|
|
|
public function getDeleteAccount(): IContent
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var User $user
|
|
|
|
*/
|
|
|
|
$user = $this->request->user();
|
|
|
|
|
2020-07-05 00:58:03 +02:00
|
|
|
return new HtmlContent('account/delete', ['user' => $user->toArray()]);
|
2020-06-14 20:15:53 +02:00
|
|
|
}
|
|
|
|
|
2020-06-25 16:44:34 +02:00
|
|
|
public function saveAccount(): IContent
|
2020-06-14 20:15:53 +02:00
|
|
|
{
|
2020-06-18 00:21:18 +02:00
|
|
|
/**
|
|
|
|
* @var User $user
|
|
|
|
*/
|
2020-06-14 20:15:53 +02:00
|
|
|
$user = $this->request->user();
|
|
|
|
|
2020-06-28 03:18:51 +02:00
|
|
|
if (!$this->confirmUserIdentity(
|
|
|
|
$user,
|
|
|
|
$this->request->session()->get('authenticated_with_google_until'),
|
|
|
|
$this->request->post('password'),
|
|
|
|
$error
|
|
|
|
)) {
|
2020-07-05 00:58:03 +02:00
|
|
|
return new JsonContent(['error' => ['errorText' => $error]]);
|
2020-06-14 20:15:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen($this->request->post('password_new')) > 0) {
|
|
|
|
if (strlen($this->request->post('password_new')) < 6) {
|
2020-07-05 00:58:03 +02:00
|
|
|
return new JsonContent([
|
|
|
|
'error' => [
|
|
|
|
'errorText' => 'The given new password is too short. Please choose a password that is at least 6 characters long!'
|
|
|
|
]
|
|
|
|
]);
|
2020-06-14 20:15:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->request->post('password_new') !== $this->request->post('password_new_confirm')) {
|
2020-07-05 00:58:03 +02:00
|
|
|
return new JsonContent([
|
|
|
|
'error' => [
|
|
|
|
'errorText' => 'The given new passwords do not match.'
|
|
|
|
]
|
|
|
|
]);
|
2020-06-14 20:15:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$user->setPlainPassword($this->request->post('password_new'));
|
|
|
|
}
|
|
|
|
|
2020-06-18 00:21:18 +02:00
|
|
|
$this->pdm->saveToDb($user);
|
2020-06-14 20:15:53 +02:00
|
|
|
|
2020-06-28 03:18:51 +02:00
|
|
|
$this->request->session()->delete('authenticated_with_google_until');
|
|
|
|
|
2020-07-05 00:58:03 +02:00
|
|
|
return new JsonContent(['success' => true]);
|
2020-06-14 20:15:53 +02:00
|
|
|
}
|
2020-06-25 20:26:33 +02:00
|
|
|
|
|
|
|
public function deleteAccount(): IContent
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var User $user
|
|
|
|
*/
|
|
|
|
$user = $this->request->user();
|
|
|
|
|
2020-06-28 03:18:51 +02:00
|
|
|
if (!$this->confirmUserIdentity(
|
|
|
|
$user,
|
|
|
|
$this->request->session()->get('authenticated_with_google_until'),
|
|
|
|
$this->request->post('password'),
|
|
|
|
$error
|
|
|
|
)) {
|
2020-07-05 00:58:03 +02:00
|
|
|
return new JsonContent(['error' => ['errorText' => $error]]);
|
2020-06-25 20:26:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
\Container::$dbConnection->startTransaction();
|
|
|
|
|
2020-07-05 13:32:15 +02:00
|
|
|
$userConfirmation = $this->userConfirmationRepository->getByUser($user);
|
|
|
|
if ($userConfirmation !== null) {
|
2020-06-25 20:26:33 +02:00
|
|
|
$this->pdm->deleteFromDb($userConfirmation);
|
|
|
|
}
|
|
|
|
|
2020-07-05 13:22:22 +02:00
|
|
|
$userPasswordResetter = $this->userPasswordResetterRepository->getByUser($user);
|
|
|
|
if ($userPasswordResetter !== null) {
|
2020-07-05 00:09:45 +02:00
|
|
|
$this->pdm->deleteFromDb($userPasswordResetter);
|
|
|
|
}
|
|
|
|
|
2020-06-25 20:26:33 +02:00
|
|
|
$this->pdm->deleteFromDb($user);
|
|
|
|
|
|
|
|
\Container::$dbConnection->commit();
|
|
|
|
|
2020-06-28 03:18:51 +02:00
|
|
|
$this->request->session()->delete('authenticated_with_google_until');
|
|
|
|
|
2020-07-05 00:58:03 +02:00
|
|
|
return new JsonContent(['success' => true]);
|
2020-06-25 20:26:33 +02:00
|
|
|
}
|
2020-06-28 03:18:51 +02:00
|
|
|
|
2020-07-05 21:47:32 +02:00
|
|
|
private function confirmUserIdentity(User $user, ?DateTime $authenticatedWithGoogleUntil, ?string $password, string &$error): bool
|
2020-06-28 03:18:51 +02:00
|
|
|
{
|
|
|
|
if ($authenticatedWithGoogleUntil !== null && $authenticatedWithGoogleUntil > new DateTime()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($password !== null) {
|
|
|
|
if ($user->checkPassword($password)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$error = 'The given current password is wrong.';
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$error = 'Could not confirm your identity. Please try again!';
|
|
|
|
return false;
|
|
|
|
}
|
2020-06-14 20:15:53 +02:00
|
|
|
}
|