move to plugin

This commit is contained in:
Diogo Peralta Cordeiro 2022-01-16 17:56:54 +00:00
parent 4ad1de2616
commit 5ac764f3e5
Signed by: diogo
GPG Key ID: 18D2D35001FBFAB0
3 changed files with 94 additions and 77 deletions

View File

@ -33,10 +33,23 @@ declare(strict_types = 1);
namespace Plugin\OAuth2; namespace Plugin\OAuth2;
use App\Core\Event; use App\Core\Event;
use App\Core\Log;
use App\Core\Modules\Plugin; use App\Core\Modules\Plugin;
use App\Core\Router\RouteLoader; use App\Core\Router\RouteLoader;
use App\Core\Router\Router; use App\Core\Router\Router;
use App\Util\Common;
use App\Util\Exception\NoLoggedInUser;
use App\Util\Formatting;
use Nyholm\Psr7\Response;
use Plugin\OAuth2\Controller\Apps; use Plugin\OAuth2\Controller\Apps;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Trikoder\Bundle\OAuth2Bundle\Event\AuthorizationRequestResolveEvent;
use Trikoder\Bundle\OAuth2Bundle\Event\UserResolveEvent;
use Trikoder\Bundle\OAuth2Bundle\OAuth2Events;
use Trikoder\Bundle\OAuth2Bundle\OAuth2Grants;
use XML_XRD_Element_Link; use XML_XRD_Element_Link;
/** /**
@ -45,7 +58,7 @@ use XML_XRD_Element_Link;
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org * @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/ */
class OAuth2 extends Plugin class OAuth2 extends Plugin implements EventSubscriberInterface
{ {
public const OAUTH_ACCESS_TOKEN_REL = 'http://apinamespace.org/oauth/access_token'; public const OAUTH_ACCESS_TOKEN_REL = 'http://apinamespace.org/oauth/access_token';
public const OAUTH_REQUEST_TOKEN_REL = 'http://apinamespace.org/oauth/request_token'; public const OAUTH_REQUEST_TOKEN_REL = 'http://apinamespace.org/oauth/request_token';
@ -80,4 +93,74 @@ class OAuth2 extends Plugin
$links[] = new XML_XRD_Element_link(self::OAUTH_ACCESS_TOKEN_REL, Router::url('oauth2_token', type: Router::ABSOLUTE_URL)); $links[] = new XML_XRD_Element_link(self::OAUTH_ACCESS_TOKEN_REL, Router::url('oauth2_token', type: Router::ABSOLUTE_URL));
return Event::next; return Event::next;
} }
public function userResolve(UserResolveEvent $event, UserProviderInterface $userProvider, UserPasswordEncoderInterface $userPasswordEncoder): void
{
Log::debug('cenas: ', [$event, $userProvider, $userPasswordEncoder]);
$user = $userProvider->loadUserByUsername($event->getUsername());
if (\is_null($user)) {
return;
}
if (!$userPasswordEncoder->isPasswordValid($user, $event->getPassword())) {
return;
}
$event->setUser($user);
}
public function authorizeRequestResolve(AuthorizationRequestResolveEvent $event): void
{
$request = Common::getRequest();
try {
$user = Common::ensureLoggedIn();
// get requests will be intercepted and shown the login form
// other verbs we will handle as an authorization denied
// and this implementation ensures a user is set at this point already
if ($request->getMethod() !== 'POST') {
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_DENIED);
return;
} else {
if (!$request->request->has('action')) {
// 1. successful login, goes to grant page
$content = Formatting::twigRenderFile('security/grant.html.twig', [
'scopes' => $event->getScopes(),
'client' => $event->getClient(),
'grant' => OAuth2Grants::AUTHORIZATION_CODE,
// very simple way to ensure user gets to this point in the
// flow when granting or denying is to pre-add their credentials
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
]);
$response = new Response(200, [], $content);
$event->setResponse($response);
} else {
// 2. grant operation, either grants or denies
if ($request->request->get('action') === OAuth2Grants::AUTHORIZATION_CODE) {
$event->setUser($user);
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_APPROVED);
} else {
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_DENIED);
}
}
}
// Whoops!
throw new BadRequestException();
} catch (NoLoggedInUser) {
$event->setResponse(new Response(302, [
'Location' => Router::url('security_login', [
'returnUrl' => $request->getUri(),
]),
]));
}
}
public static function getSubscribedEvents(): array
{
return [
OAuth2Events::USER_RESOLVE => 'userResolve',
OAuth2Events::AUTHORIZATION_REQUEST_RESOLVE => 'authorizeRequestResolve',
];
}
} }

View File

@ -52,12 +52,10 @@ use App\Kernel;
use App\Security\EmailVerifier; use App\Security\EmailVerifier;
use App\Util\Common; use App\Util\Common;
use App\Util\Exception\ConfigurationException; use App\Util\Exception\ConfigurationException;
use App\Util\Exception\NoLoggedInUser;
use App\Util\Formatting; use App\Util\Formatting;
use App\Util\HTML; use App\Util\HTML;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use HtmlSanitizer\SanitizerInterface; use HtmlSanitizer\SanitizerInterface;
use Nyholm\Psr7\Response;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Event\ConsoleCommandEvent;
@ -66,7 +64,6 @@ use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
@ -75,18 +72,12 @@ use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Security as SSecurity; use Symfony\Component\Security\Core\Security as SSecurity;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait; use Symfony\Component\Security\Http\Util\TargetPathTrait;
use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface; use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
use SymfonyCasts\Bundle\VerifyEmail\VerifyEmailHelperInterface; use SymfonyCasts\Bundle\VerifyEmail\VerifyEmailHelperInterface;
use Trikoder\Bundle\OAuth2Bundle\Event\AuthorizationRequestResolveEvent;
use Trikoder\Bundle\OAuth2Bundle\Event\UserResolveEvent;
use Trikoder\Bundle\OAuth2Bundle\OAuth2Events;
use Trikoder\Bundle\OAuth2Bundle\OAuth2Grants;
use Twig\Environment; use Twig\Environment;
/** /**
@ -275,69 +266,6 @@ class GNUsocial implements EventSubscriberInterface
$container->setParameter('gnusocial_defaults', $defaults); $container->setParameter('gnusocial_defaults', $defaults);
} }
public function userResolve(UserResolveEvent $event, UserProviderInterface $userProvider, UserPasswordEncoderInterface $userPasswordEncoder): void
{
Log::debug('cenas: ', [$event, $userProvider, $userPasswordEncoder]);
$user = $userProvider->loadUserByUsername($event->getUsername());
if (\is_null($user)) {
return;
}
if (!$userPasswordEncoder->isPasswordValid($user, $event->getPassword())) {
return;
}
$event->setUser($user);
}
public function authorizeRequestResolve(AuthorizationRequestResolveEvent $event): void
{
$request = $this->request;
try {
$user = Common::ensureLoggedIn();
// get requests will be intercepted and shown the login form
// other verbs we will handle as an authorization denied
// and this implementation ensures a user is set at this point already
if ($request->getMethod() !== 'POST') {
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_DENIED);
return;
} else {
if (!$request->request->has('action')) {
// 1. successful login, goes to grant page
$content = $this->twig->render('security/grant.html.twig', [
'scopes' => $event->getScopes(),
'client' => $event->getClient(),
'grant' => OAuth2Grants::AUTHORIZATION_CODE,
// very simple way to ensure user gets to this point in the
// flow when granting or denying is to pre-add their credentials
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
]);
$response = new Response(200, [], $content);
$event->setResponse($response);
} else {
// 2. grant operation, either grants or denies
if ($request->request->get('action') === OAuth2Grants::AUTHORIZATION_CODE) {
$event->setUser($user);
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_APPROVED);
} else {
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_DENIED);
}
}
}
// Whoops!
throw new BadRequestException();
} catch (NoLoggedInUser) {
$event->setResponse(new Response(302, [
'Location' => Router::url('security_login', [
'returnUrl' => $request->getUri(),
]),
]));
}
}
/** /**
* Tell Symfony which events we want to listen to, which Symfony detects and auto-wires * Tell Symfony which events we want to listen to, which Symfony detects and auto-wires
* due to this implementing the `EventSubscriberInterface` * due to this implementing the `EventSubscriberInterface`
@ -345,10 +273,8 @@ class GNUsocial implements EventSubscriberInterface
public static function getSubscribedEvents(): array public static function getSubscribedEvents(): array
{ {
return [ return [
KernelEvents::REQUEST => 'onKernelRequest', KernelEvents::REQUEST => 'onKernelRequest',
'console.command' => 'onCommand', 'console.command' => 'onCommand',
OAuth2Events::USER_RESOLVE => 'userResolve',
OAuth2Events::AUTHORIZATION_REQUEST_RESOLVE => 'authorizeRequestResolve',
]; ];
} }
} }

View File

@ -67,6 +67,14 @@ abstract class Common
self::$request = $req; self::$request = $req;
} }
/**
* Don't use this
*/
public static function getRequest(): ?Request
{
return self::$request;
}
public static function route() public static function route()
{ {
return self::$request->attributes->get('_route'); return self::$request->attributes->get('_route');