. // }}} namespace App\Security; use App\Core\DB\DB; use function App\Core\I18n\_m; use App\Core\Log; use App\Entity\User; use App\Util\Nickname; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; use Symfony\Component\Security\Http\Util\TargetPathTrait; /** * User authenticator * * @category Authentication * @package GNUsocial * * @author Hugo Sales * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ class Authenticator extends AbstractFormLoginAuthenticator { use TargetPathTrait; public const LOGIN_ROUTE = 'login'; private $entityManager; private $urlGenerator; private $csrfTokenManager; public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager) { $this->entityManager = $entityManager; $this->urlGenerator = $urlGenerator; $this->csrfTokenManager = $csrfTokenManager; } public function supports(Request $request) { return self::LOGIN_ROUTE === $request->attributes->get('_route') && $request->isMethod('POST'); } public function getCredentials(Request $request) { $credentials = [ 'nickname' => $request->request->get('nickname'), 'password' => $request->request->get('password'), 'csrf_token' => $request->request->get('_csrf_token'), ]; $request->getSession()->set( Security::LAST_USERNAME, $credentials['nickname'] ); return $credentials; } public function getUser($credentials, UserProviderInterface $userProvider) { $token = new CsrfToken('authenticate', $credentials['csrf_token']); if (!$this->csrfTokenManager->isTokenValid($token)) { throw new InvalidCsrfTokenException(); } $nick = Nickname::normalize($credentials['nickname']); $user = DB::findOneBy('local_user', ['or' => ['nickname' => $nick, 'outgoing_email' => $nick]]); if (!$user) { throw new CustomUserMessageAuthenticationException( _m('Either \'{nickname}\' doesn\'t match any registered nickname or email, or the supplied password is incorrect.', ['{nickname}' => $credentials['nickname']])); } return $user; } public function checkCredentials($credentials, UserInterface $user) { $password = $user->getPassword(); Log::error(print_r($user, true)); // crypt understands what the salt part of $user->password is if ($password === crypt($credentials['password'], $user->password)) { $this->changePassword($user->nickname, null, $password); return $user; } // If we check StatusNet hash, for backwards compatibility and migration if ($this->statusnet && $user->password === md5($password . $user->id)) { // and update password hash entry to crypt() compatible if ($this->overwrite) { $this->changePassword($user->nickname, null, $password); } return $user; } // Timing safe password verification on supported PHP versions if (password_verify($password, $user->password)) { return $user; } return false; } public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) { if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) { return new RedirectResponse($targetPath); } // For example : return new RedirectResponse($this->urlGenerator->generate('some_route')); throw new \Exception('TODO: provide a valid redirect inside ' . __FILE__); } protected function getLoginUrl() { return $this->urlGenerator->generate(self::LOGIN_ROUTE); } }