gnu-social/src/Core/Security.php

117 lines
4.1 KiB
PHP

<?php
declare(strict_types = 1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* Wrapper around Symfony's Security service, for static access
*
* @package GNUsocial
* @category Security
*
* @author Hugo Sales <hugo@hsal.es>
* @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");
}
}
}