Merge "Add support for multiple shellbox URLs through a mapping configuration"

This commit is contained in:
jenkins-bot 2021-06-23 13:22:31 +00:00 committed by Gerrit Code Review
commit b42fabf55d
6 changed files with 131 additions and 16 deletions

View file

@ -28,6 +28,8 @@ For notes on 1.36.x and older releases, see HISTORY.
* $wgContentHandlerTextFallback - This setting, which defines how to
react if a plain text version of a non-text Content object is
requested using ContentHandler::getContentText(), is deprecated.
* $wgShellboxUrl is deprecated, use $wgShellboxUrls as a mapping of
service => URL instead.
* …
==== Removed configuration ====

View file

@ -9580,17 +9580,25 @@ $wgShellLocale = 'C.UTF-8';
*/
$wgShellRestrictionMethod = 'autodetect';
/**
* @deprecated since 1.37; use $wgShellboxUrls instead
*/
$wgShellboxUrl = null;
/**
* Shell commands can be run on a remote server using Shellbox. To use this
* feature, set this to the URL, and also configure $wgShellboxSecretKey.
* feature, set this to the URLs mapped by the service, and also configure $wgShellboxSecretKey.
* You can also disable a certain service by setting it to false or null.
*
* 'default' would be the default URL if no URL is defined for that service.
*
* For more information about installing Shellbox, see
* https://www.mediawiki.org/wiki/Shellbox
*
* @since 1.36
* @var string|null
* @since 1.37
* @var (string|false|null)[]
*/
$wgShellboxUrl = null;
$wgShellboxUrls = [ 'default' => null ];
/**
* The secret key for HMAC verification of Shellbox requests. Set this to

View file

@ -1323,9 +1323,15 @@ return [
},
'ShellboxClientFactory' => static function ( MediaWikiServices $services ) : ShellboxClientFactory {
$urls = $services->getMainConfig()->get( 'ShellboxUrls' );
// TODO: Remove this logic and $wgShellboxUrl configuration in 1.38
$url = $services->getMainConfig()->get( 'ShellboxUrl' );
if ( $url !== null ) {
$urls['default'] = $url;
}
return new ShellboxClientFactory(
$services->getHttpRequestFactory(),
$services->getMainConfig()->get( 'ShellboxUrl' ),
$urls,
$services->getMainConfig()->get( 'ShellboxSecretKey' )
);
},

View file

@ -15,8 +15,8 @@ use Shellbox\Client;
class ShellboxClientFactory {
/** @var HttpRequestFactory */
private $requestFactory;
/** @var string|null */
private $url;
/** @var (string|false|null)[]|null */
private $urls;
/** @var string|null */
private $key;
@ -27,22 +27,23 @@ class ShellboxClientFactory {
* @internal Use MediaWikiServices::getShellboxClientFactory()
* @param HttpRequestFactory $requestFactory The factory which will be used
* to make HTTP clients.
* @param string|null $url The Shellbox base URL
* @param (string|false|null)[]|null $urls The Shellbox base URL mapping
* @param string|null $key The shared secret key used for HMAC authentication
*/
public function __construct( HttpRequestFactory $requestFactory, $url, $key ) {
public function __construct( HttpRequestFactory $requestFactory, $urls, $key ) {
$this->requestFactory = $requestFactory;
$this->url = $url;
$this->urls = $urls;
$this->key = $key;
}
/**
* Test whether remote Shellbox is enabled by configuration.
*
* @param string|null $service Same as the service option for getClient.
* @return bool
*/
public function isEnabled() {
return $this->url !== null && strlen( $this->key );
public function isEnabled( ?string $service = null ): bool {
return $this->getUrl( $service ) !== null;
}
/**
@ -51,20 +52,34 @@ class ShellboxClientFactory {
*
* @param array $options Associative array of options:
* - timeout: The request timeout in seconds
* - service: the shellbox backend name to get the URL from the mapping
* @return Client
* @throws \RuntimeException
*/
public function getClient( array $options = [] ) {
if ( !$this->isEnabled() ) {
$url = $this->getUrl( $options['service'] ?? null );
if ( $url === null ) {
throw new \RuntimeException( 'To use a remote shellbox to run shell commands, ' .
'$wgShellboxUrl and $wgShellboxSecretKey must be configured.' );
'$wgShellboxUrls and $wgShellboxSecretKey must be configured.' );
}
return new Client(
new ShellboxHttpClient( $this->requestFactory,
$options['timeout'] ?? self::DEFAULT_TIMEOUT ),
new Uri( $this->url ),
new Uri( $url ),
$this->key
);
}
private function getUrl( ?string $service ): ?string {
if ( $this->urls === null || !strlen( $this->key ) ) {
return null;
}
$url = $this->urls[$service] ?? $this->urls['default'] ?? null;
if ( !is_string( $url ) ) {
return null;
}
return $url;
}
}

View file

@ -28,7 +28,7 @@ class CommandFactoryTest extends MediaWikiUnitTestCase {
public function __construct() {
}
public function isEnabled() {
public function isEnabled( ?string $service = null ): bool {
return false;
}

View file

@ -0,0 +1,84 @@
<?php
use MediaWiki\Http\HttpRequestFactory;
use MediaWiki\Shell\ShellboxClientFactory;
/**
* @group Shell
* @covers MediaWiki\Shell\ShellboxClientFactory
*/
class ShellboxClientFactoryTest extends MediaWikiUnitTestCase {
/** @dataProvider provideEnabledArgs */
public function testIsEnabled( ?array $urls, ?string $service, bool $expected ): void {
$shellboxClientFactory = new ShellboxClientFactory(
$this->createMock( HttpRequestFactory::class ),
$urls,
'key'
);
$actual = $shellboxClientFactory->isEnabled( $service );
$this->assertSame( $expected, $actual );
}
public function provideEnabledArgs(): iterable {
yield 'not configured, default service' => [
'urls' => null,
'service' => null,
'expected' => false,
];
yield 'not configured, custom service' => [
'urls' => null,
'service' => 'custom',
'expected' => false,
];
yield 'default configured, default service' => [
'urls' => [ 'default' => 'http://example.com' ],
'service' => null,
'expected' => true,
];
yield 'default configured, custom service' => [
'urls' => [ 'default' => 'http://example.com' ],
'service' => 'custom',
'expected' => true,
];
yield 'custom configured, custom service' => [
'urls' => [ 'custom' => 'http://example.com' ],
'service' => 'custom',
'expected' => true,
];
yield 'custom disabled, default service' => [
'urls' => [
'default' => 'http://example.com',
'custom' => false,
],
'service' => 'default',
'expected' => true,
];
yield 'custom disabled, custom service' => [
'urls' => [
'default' => 'http://example.com',
'custom' => false,
],
'service' => 'custom',
'expected' => false,
];
}
public function testIsEnabledWithoutKey(): void {
$shellboxClientFactory = new ShellboxClientFactory(
$this->createMock( HttpRequestFactory::class ),
[ 'default' => 'http://example.com' ],
null
);
$this->assertFalse( $shellboxClientFactory->isEnabled() );
}
}