214 lines
7.0 KiB
PHP
214 lines
7.0 KiB
PHP
<?php namespace RVR\Controller;
|
|
|
|
use DateTime;
|
|
use SokoWeb\Http\Request;
|
|
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
|
|
use SokoWeb\Interfaces\Request\IRequest;
|
|
use SokoWeb\Interfaces\Response\IContent;
|
|
use SokoWeb\Interfaces\Response\IRedirect;
|
|
use SokoWeb\OAuth\GoogleOAuth;
|
|
use SokoWeb\PersistentData\PersistentDataManager;
|
|
use RVR\PersistentData\Model\User;
|
|
use SokoWeb\Response\HtmlContent;
|
|
use SokoWeb\Response\JsonContent;
|
|
use SokoWeb\Response\Redirect;
|
|
use SokoWeb\Util\JwtParser;
|
|
use RVR\Repository\UserRepository;
|
|
|
|
class UserController implements IAuthenticationRequired
|
|
{
|
|
private IRequest $request;
|
|
|
|
private PersistentDataManager $pdm;
|
|
|
|
private UserRepository $userRepository;
|
|
|
|
public function __construct(IRequest $request)
|
|
{
|
|
$this->request = $request;
|
|
$this->pdm = new PersistentDataManager();
|
|
$this->userRepository = new UserRepository();
|
|
}
|
|
|
|
public function isAuthenticationRequired(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function getAccount(): IContent
|
|
{
|
|
/**
|
|
* @var User $user
|
|
*/
|
|
$user = $this->request->user();
|
|
|
|
return new HtmlContent('account/account', ['user' => $user->toArray()]);
|
|
}
|
|
|
|
public function getGoogleAuthenticateRedirect(): IRedirect
|
|
{
|
|
/**
|
|
* @var User $user
|
|
*/
|
|
$user = $this->request->user();
|
|
|
|
$state = bin2hex(random_bytes(16));
|
|
$nonce = bin2hex(random_bytes(16));
|
|
|
|
$this->request->session()->set('oauth_state', $state);
|
|
$this->request->session()->set('oauth_nonce', $nonce);
|
|
|
|
$oAuth = new GoogleOAuth(new Request());
|
|
|
|
$url = $oAuth->getDialogUrl(
|
|
$state,
|
|
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink(),
|
|
$nonce,
|
|
$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')) {
|
|
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
|
}
|
|
|
|
$oAuth = new GoogleOAuth(new Request());
|
|
$tokenData = $oAuth->getToken(
|
|
$this->request->query('code'),
|
|
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink()
|
|
);
|
|
|
|
if (!isset($tokenData['id_token'])) {
|
|
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
|
}
|
|
|
|
$jwtParser = new JwtParser($tokenData['id_token']);
|
|
$idToken = $jwtParser->getPayload();
|
|
|
|
if ($idToken['nonce'] !== $this->request->session()->get('oauth_nonce')) {
|
|
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
|
}
|
|
|
|
if ($idToken['sub'] !== $user->getGoogleSub()) {
|
|
return new HtmlContent('account/google_authenticate', [
|
|
'success' => false,
|
|
'errorText' => 'This Google account is not linked to your account.'
|
|
]);
|
|
}
|
|
|
|
$authenticatedWithGoogleUntil = new DateTime('+45 seconds');
|
|
$this->request->session()->set('authenticated_with_google_until', $authenticatedWithGoogleUntil);
|
|
|
|
return new HtmlContent('account/google_authenticate', [
|
|
'success' => true,
|
|
'authenticatedWithGoogleUntil' => $authenticatedWithGoogleUntil
|
|
]);
|
|
}
|
|
|
|
public function saveAccount(): IContent
|
|
{
|
|
/**
|
|
* @var User $user
|
|
*/
|
|
$user = $this->request->user();
|
|
|
|
if (!$this->confirmUserIdentity(
|
|
$user,
|
|
$this->request->session()->get('authenticated_with_google_until'),
|
|
$this->request->post('password'),
|
|
$error
|
|
)) {
|
|
return new JsonContent(['error' => ['errorText' => $error]]);
|
|
}
|
|
|
|
$newEmail = $this->request->post('email');
|
|
if ($newEmail !== $user->getEmail()) {
|
|
if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
|
|
return new JsonContent(['error' => ['errorText' => 'Please provide a valid email address.']]);
|
|
}
|
|
|
|
if ($this->userRepository->getByEmail($newEmail) !== null) {
|
|
return new JsonContent(['error' => ['errorText' => 'The given email address belongs to another account.']]);
|
|
}
|
|
|
|
$user->setEmail($newEmail);
|
|
}
|
|
|
|
$newUsername = $this->request->post('username');
|
|
if ($newUsername !== $user->getUsername()) {
|
|
if (strlen($newUsername) > 0) {
|
|
if (filter_var($newUsername, FILTER_VALIDATE_EMAIL)) {
|
|
return new JsonContent(['error' => ['errorText' => 'Please select a username that is not a valid email address.']]);
|
|
}
|
|
|
|
if ($this->userRepository->getByUsername($newUsername) !== null) {
|
|
return new JsonContent(['error' => ['errorText' => 'The given username is already taken.']]);
|
|
}
|
|
|
|
$user->setUsername($newUsername);
|
|
} else {
|
|
$user->setUsername(null);
|
|
}
|
|
}
|
|
|
|
$newPassword = $this->request->post('password_new');
|
|
if (strlen($newPassword) > 0) {
|
|
if (strlen($newPassword) < 6) {
|
|
return new JsonContent([
|
|
'error' => [
|
|
'errorText' => 'The given new password is too short. Please choose a password that is at least 6 characters long!'
|
|
]
|
|
]);
|
|
}
|
|
|
|
if ($newPassword !== $this->request->post('password_new_confirm')) {
|
|
return new JsonContent([
|
|
'error' => [
|
|
'errorText' => 'The given new passwords do not match.'
|
|
]
|
|
]);
|
|
}
|
|
|
|
$user->setPlainPassword($newPassword);
|
|
}
|
|
|
|
$user->setNickname($this->request->post('nickname'));
|
|
$user->setPhone($this->request->post('phone'));
|
|
$user->setIdNumber($this->request->post('id_number'));
|
|
|
|
$this->pdm->saveToDb($user);
|
|
|
|
$this->request->session()->delete('authenticated_with_google_until');
|
|
|
|
return new JsonContent(['success' => true]);
|
|
}
|
|
|
|
private function confirmUserIdentity(User $user, ?DateTime $authenticatedWithGoogleUntil, ?string $password, ?string &$error): bool
|
|
{
|
|
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;
|
|
}
|
|
}
|