Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
52 / 52
http_auth_subscriber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
5 / 5
20
100.00% covered (success)
100.00%
52 / 52
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
6 / 6
 on_kernel_request
100.00% covered (success)
100.00%
1 / 1
9
100.00% covered (success)
100.00%
24 / 24
 get_credentials
100.00% covered (success)
100.00%
1 / 1
8
100.00% covered (success)
100.00%
15 / 15
 send_auth_challenge
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
6 / 6
 getSubscribedEvents
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\feed\event;
use phpbb\auth\auth;
use phpbb\config\config;
use phpbb\language\language;
use phpbb\request\request_interface;
use phpbb\user;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
 * Event subscriber for HTTP authentication on feed routes
 */
class http_auth_subscriber implements EventSubscriberInterface
{
    /** @var auth */
    protected $auth;
    /** @var config */
    protected $config;
    /** @var language */
    protected $language;
    /** @var request_interface */
    protected $request;
    /** @var user */
    protected $user;
    /**
     * Constructor
     *
     * @param auth                $auth        Auth object
     * @param config            $config        Config object
     * @param language    $language    Language object
     * @param request_interface    $request    Request object
     * @param user                $user        User object
     */
    public function __construct(auth $auth, config $config, language $language, request_interface $request, user $user)
    {
        $this->auth = $auth;
        $this->config = $config;
        $this->language = $language;
        $this->request = $request;
        $this->user = $user;
    }
    /**
     * Handle HTTP authentication for feed routes
     *
     * @param GetResponseEvent $event
     * @return void
     */
    public function on_kernel_request(GetResponseEvent $event)
    {
        // Check if HTTP authentication is enabled
        if (!$this->config['feed_http_auth'])
        {
            return;
        }
        $request = $event->getRequest();
        $route = $request->attributes->get('_route');
        // Only apply to feed routes
        if (strpos($route, 'phpbb_feed_') !== 0)
        {
            return;
        }
        // Only allow HTTP authentication in secure context (HTTPS)
        if (!$request->isSecure())
        {
            return;
        }
        // User is already logged in, no need to authenticate
        if (!empty($this->user->data['is_registered']))
        {
            return;
        }
        // Get HTTP authentication credentials
        [$username, $password] = $this->get_credentials();
        // If no credentials provided, send authentication challenge
        if ($username === null || $password === null)
        {
            $this->send_auth_challenge($event);
            return;
        }
        // Attempt to login with the provided credentials
        $auth_result = $this->auth->login($username, $password, false, true, false);
        if ($auth_result['status'] == LOGIN_SUCCESS)
        {
            // Reload ACL for the newly logged-in user
            $this->auth->acl($this->user->data);
            return;
        }
        else if ($auth_result['status'] == LOGIN_ERROR_ATTEMPTS)
        {
            // Too many login attempts
            $response = new Response($this->language->lang('LOGIN_ERROR_ATTEMPTS'), Response::HTTP_UNAUTHORIZED);
            $event->setResponse($response);
            return;
        }
        // Authentication failed, send challenge
        $this->send_auth_challenge($event);
    }
    /**
     * Retrieve HTTP authentication credentials from server variables
     *
     * @return array [username, password] Array containing the username and password, or null if not found
     */
    protected function get_credentials(): array
    {
        $username_keys = [
            'PHP_AUTH_USER',
            'Authorization',
            'REMOTE_USER',
            'REDIRECT_REMOTE_USER',
            'HTTP_AUTHORIZATION',
            'REDIRECT_HTTP_AUTHORIZATION',
            'REMOTE_AUTHORIZATION',
            'REDIRECT_REMOTE_AUTHORIZATION',
            'AUTH_USER',
        ];
        $password_keys = [
            'PHP_AUTH_PW',
            'REMOTE_PASSWORD',
            'AUTH_PASSWORD',
        ];
        $username = null;
        foreach ($username_keys as $key)
        {
            if ($this->request->is_set($key, request_interface::SERVER))
            {
                $username = htmlspecialchars_decode($this->request->server($key));
                break;
            }
        }
        $password = null;
        foreach ($password_keys as $key)
        {
            if ($this->request->is_set($key, request_interface::SERVER))
            {
                $password =  htmlspecialchars_decode($this->request->server($key));
                break;
            }
        }
        // Decode Basic authentication header if needed
        if (!is_null($username) && is_null($password) && strpos($username, 'Basic ') === 0)
        {
            [$username, $password] = explode(':', base64_decode(substr($username, 6)), 2);
        }
        return [$username, $password];
    }
    /**
     * Send HTTP authentication challenge
     *
     * @param GetResponseEvent $event
     * @return void
     */
    protected function send_auth_challenge(GetResponseEvent $event)
    {
        $realm = $this->config['sitename'];
        // Filter out non-ASCII characters per RFC2616
        $realm = preg_replace('/[\x80-\xFF]/', '?', $realm);
        $response = new Response($this->language->lang('NOT_AUTHORISED'), Response::HTTP_UNAUTHORIZED);
        $response->headers->set('WWW-Authenticate', 'Basic realm="' . $realm . ' - Feed"');
        $event->setResponse($response);
    }
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => ['on_kernel_request', 5],
        ];
    }
}