1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-10-12 23:44:32 +02:00

[ticket/15085] Add HTTP authentication subscriber for feed controllers

PHPBB-15085
This commit is contained in:
Marc Alexander
2025-10-03 22:26:04 +02:00
parent 34868aa4ad
commit 3a0060552a
2 changed files with 252 additions and 0 deletions

View File

@@ -15,6 +15,16 @@ services:
- '@language' - '@language'
- '%core.php_ext%' - '%core.php_ext%'
feed.http_auth_subscriber:
class: phpbb\feed\event\http_auth_subscriber
arguments:
- '@auth'
- '@config'
- '@request'
- '@user'
tags:
- { name: kernel.event_subscriber }
feed.helper: feed.helper:
class: phpbb\feed\helper class: phpbb\feed\helper
arguments: arguments:

View File

@@ -0,0 +1,242 @@
<?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\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 request_interface */
protected $request;
/** @var user */
protected $user;
/**
* Constructor
*
* @param auth $auth Auth object
* @param config $config Config object
* @param request_interface $request Request object
* @param user $user User object
*/
public function __construct(auth $auth, config $config, request_interface $request, user $user)
{
$this->auth = $auth;
$this->config = $config;
$this->request = $request;
$this->user = $user;
}
/**
* Handle HTTP authentication for feed routes
*
* @param GetResponseEvent $event
* @return void
*/
public function on_kernel_request(GetResponseEvent $event)
{
$request = $event->getRequest();
$route = $request->attributes->get('_route');
// Only apply to feed routes
if (strpos($route, 'phpbb_feed_') !== 0)
{
return;
}
// Check if HTTP authentication is enabled
if (!$this->config['feed_http_auth'])
{
return;
}
// User is already logged in, no need to authenticate
if (!empty($this->user->data['is_registered']))
{
return;
}
// Get HTTP authentication credentials
$username = $this->get_http_username();
$password = $this->get_http_password();
// 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)
{
return;
}
else if ($auth_result['status'] == LOGIN_ERROR_ATTEMPTS)
{
// Too many login attempts
$response = new Response('', Response::HTTP_UNAUTHORIZED);
$event->setResponse($response);
return;
}
// Authentication failed, send challenge
$this->send_auth_challenge($event);
}
/**
* Get HTTP username from request headers
*
* @return string|null
*/
protected function get_http_username()
{
$username_keys = array(
'PHP_AUTH_USER',
'Authorization',
'REMOTE_USER',
'REDIRECT_REMOTE_USER',
'HTTP_AUTHORIZATION',
'REDIRECT_HTTP_AUTHORIZATION',
'REMOTE_AUTHORIZATION',
'REDIRECT_REMOTE_AUTHORIZATION',
'AUTH_USER',
);
foreach ($username_keys as $key)
{
if ($this->request->is_set($key, request_interface::SERVER))
{
$username = html_entity_decode($this->request->server($key), ENT_COMPAT);
// Decode Basic authentication header
if (strpos($username, 'Basic ') === 0)
{
$credentials = base64_decode(substr($username, 6));
if (strpos($credentials, ':') !== false)
{
list($username, ) = explode(':', $credentials, 2);
}
}
return $username;
}
}
return null;
}
/**
* Get HTTP password from request headers
*
* @return string|null
*/
protected function get_http_password()
{
$password_keys = array(
'PHP_AUTH_PW',
'REMOTE_PASSWORD',
'AUTH_PASSWORD',
);
foreach ($password_keys as $key)
{
if ($this->request->is_set($key, request_interface::SERVER))
{
return html_entity_decode($this->request->server($key), ENT_COMPAT);
}
}
// Check if password is in Authorization header (Basic auth)
$username_keys = array(
'PHP_AUTH_USER',
'Authorization',
'REMOTE_USER',
'REDIRECT_REMOTE_USER',
'HTTP_AUTHORIZATION',
'REDIRECT_HTTP_AUTHORIZATION',
'REMOTE_AUTHORIZATION',
'REDIRECT_REMOTE_AUTHORIZATION',
'AUTH_USER',
);
foreach ($username_keys as $key)
{
if ($this->request->is_set($key, request_interface::SERVER))
{
$auth_header = html_entity_decode($this->request->server($key), ENT_COMPAT);
// Decode Basic authentication header
if (strpos($auth_header, 'Basic ') === 0)
{
$credentials = base64_decode(substr($auth_header, 6));
if (strpos($credentials, ':') !== false)
{
list(, $password) = explode(':', $credentials, 2);
return $password;
}
}
}
}
return null;
}
/**
* 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('', Response::HTTP_UNAUTHORIZED);
$response->headers->set('WWW-Authenticate', 'Basic realm="' . $realm . '"');
$event->setResponse($response);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
// Priority should be high to run after session_begin but before controller
KernelEvents::REQUEST => array('on_kernel_request', 5),
);
}
}