Merge "Add support for multiple shellbox URLs through a mapping configuration"
This commit is contained in:
commit
b42fabf55d
6 changed files with 131 additions and 16 deletions
|
|
@ -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 ====
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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' )
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class CommandFactoryTest extends MediaWikiUnitTestCase {
|
|||
public function __construct() {
|
||||
}
|
||||
|
||||
public function isEnabled() {
|
||||
public function isEnabled( ?string $service = null ): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue