. // }}} /** * Wrapper around Symfony's Security service, for static access * * @package GNUsocial * @category Security * * @author Hugo Sales * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ namespace App\Core; use App\Entity\LocalUser; use App\Util\Common; use App\Util\Formatting; use BadMethodCallException; use Functional as F; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Security as SymfonySecurity; use Symfony\Component\Security\Http\Event\LoginFailureEvent; use Symfony\Component\Security\Http\Event\LoginSuccessEvent; /** * Forwards method calls to either Symfony\Component\Security\Core\Security or * HtmlSanitizer\SanitizerInterface, calling the first existing method, in that order * * @codeCoverageIgnore * @mixin SymfonySecurity * * @method static LocalUser getUser() */ class Security implements EventSubscriberInterface //implements AuthenticatorInterface { private static ?SymfonySecurity $security; public static function setHelper($sec): void { self::$security = $sec; } public function loginSucess(LoginSuccessEvent $event): LoginSuccessEvent { Event::handle('LoginSuccess', [$event]); return $event; } public function loginFailure(LoginFailureEvent $event): LoginFailureEvent { Event::handle('LoginFailure', [$event]); return $event; } public static function getSubscribedEvents(): array { return [ LoginSuccessEvent::class => 'loginSucess', LoginFailureEvent::class => 'loginFailure', ]; } /** * Harden running instance. Called once from `index.php` */ public static function harden(): void { // Remove sensitive information from the [$_ENV, $to_remove] = F\partition( $_ENV, fn ($_, string $key) => Formatting::startsWith($key, ['HTTP', 'APP', 'CONFIG']) && $key !== 'APP_SECRET', ); F\each($to_remove, fn (mixed $value, string $key) => putenv($key)); // Unset // Disable stream wrappers, that could be used in things like // `file_get_contents('https://gnu.org')`. This is done // because this is a unexpected feature for most developers, // and some wrappers can be abused. For instance, `phar://` // can be used to essentially override any class when such a // file is opened and thus provide code execution to an // attacker. Not a complete solution, since `file://`, // `php://` and `glob://` get used _somewhere_, so we can't // disable them F\each( ['http', 'https', 'ftp', 'ftps', 'compress.zlib', 'data', 'phar'], // Making this configurable might be a nice feature, but it's tricky because this happens before general initialization fn (string $protocol) => \stream_wrapper_unregister($protocol) );; } public static function __callStatic(string $name, array $args) { if (method_exists(self::$security, $name)) { return self::$security->{$name}(...$args); } else { throw new BadMethodCallException("Method Security::{$name} doesn't exist"); } } }