feat: make search path for BinaryFinder customizable.

This feature is important for nextcloud running on
distributions like NixOS, where all the standard
search paths do not exist.

Also added tests.

This fixes issue #43922

Co-authored-by: Daniel <mail@danielkesselberg.de>
Signed-off-by: Reno Reckling <e-github@wthack.de>
This commit is contained in:
Reno Reckling 2024-03-06 14:24:00 +01:00 committed by Daniel Kesselberg
parent bc5222726b
commit ef7e857881
No known key found for this signature in database
GPG key ID: 4A81C29F63464E8F
3 changed files with 118 additions and 10 deletions

View file

@ -2533,4 +2533,20 @@ $CONFIG = [
* Defaults to ``true``
*/
'enable_non-accessible_features' => true,
/**
* Directories where nextcloud looks for binaries.
* This is used to find external binaries like libreoffice, sendmail, ffmpeg and more.
*
* Defaults to ``['/usr/local/sbin','/usr/local/bin','/usr/sbin','/usr/bin','/sbin','/bin','/opt/bin']``
*/
'binary_search_paths' => [
'/usr/local/sbin',
'/usr/local/bin',
'/usr/sbin',
'/usr/bin',
'/sbin',
'/bin',
'/opt/bin',
],
];

View file

@ -11,15 +11,28 @@ namespace OC;
use OCP\IBinaryFinder;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use Symfony\Component\Process\ExecutableFinder;
/**
* Service that find the binary path for a program
*/
class BinaryFinder implements IBinaryFinder {
public const DEFAULT_BINARY_SEARCH_PATHS = [
'/usr/local/sbin',
'/usr/local/bin',
'/usr/sbin',
'/usr/bin',
'/sbin',
'/bin',
'/opt/bin',
];
private ICache $cache;
public function __construct(ICacheFactory $cacheFactory) {
public function __construct(
ICacheFactory $cacheFactory,
private IConfig $config,
) {
$this->cache = $cacheFactory->createLocal('findBinaryPath');
}
@ -37,15 +50,10 @@ class BinaryFinder implements IBinaryFinder {
if (\OCP\Util::isFunctionEnabled('exec')) {
$exeSniffer = new ExecutableFinder();
// Returns null if nothing is found
$result = $exeSniffer->find($program, null, [
'/usr/local/sbin',
'/usr/local/bin',
'/usr/sbin',
'/usr/bin',
'/sbin',
'/bin',
'/opt/bin',
]);
$result = $exeSniffer->find(
$program,
null,
$this->config->getSystemValue('binary_search_paths', self::DEFAULT_BINARY_SEARCH_PATHS));
if ($result === null) {
$result = false;
}

View file

@ -0,0 +1,84 @@
<?php
declare(strict_types = 1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace Test;
use OC\BinaryFinder;
use OC\Memcache\ArrayCache;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
class BinaryFinderTest extends TestCase {
private ICache $cache;
private ICacheFactory $cacheFactory;
private $oldEnv;
protected function setUp(): void {
$this->oldEnv = getenv('PATH');
// BinaryFinder always includes the "PATH" environment variable into the search path,
// which we want to avoid in this test because they are not usually found in webserver
// deployments.
putenv('PATH=""');
$this->cacheFactory = $this->createMock(ICacheFactory::class);
$this->cache = new ArrayCache();
$this->cacheFactory->method('createLocal')->with('findBinaryPath')->willReturn($this->cache);
}
protected function tearDown(): void {
putenv('PATH=' . $this->oldEnv);
}
public function testDefaultFindsCat() {
$config = $this->createMock(IConfig::class);
$config
->method('getSystemValue')
->with('binary_search_paths', $this->anything())
->will($this->returnCallback(function ($key, $default) {
return $default;
}));
$finder = new BinaryFinder($this->cacheFactory, $config);
$this->assertEquals($finder->findBinaryPath('cat'), '/usr/bin/cat');
$this->assertEquals($this->cache->get('cat'), '/usr/bin/cat');
}
public function testDefaultDoesNotFindCata() {
$config = $this->createMock(IConfig::class);
$config
->method('getSystemValue')
->with('binary_search_paths', $this->anything())
->will($this->returnCallback(function ($key, $default) {
return $default;
}));
$finder = new BinaryFinder($this->cacheFactory, $config);
$this->assertFalse($finder->findBinaryPath('cata'));
$this->assertFalse($this->cache->get('cata'));
}
public function testCustomPathFindsCat() {
$config = $this->createMock(IConfig::class);
$config
->method('getSystemValue')
->with('binary_search_paths', $this->anything())
->willReturn(['/usr/bin']);
$finder = new BinaryFinder($this->cacheFactory, $config);
$this->assertEquals($finder->findBinaryPath('cat'), '/usr/bin/cat');
$this->assertEquals($this->cache->get('cat'), '/usr/bin/cat');
}
public function testWrongCustomPathDoesNotFindCat() {
$config = $this->createMock(IConfig::class);
$config
->method('getSystemValue')
->with('binary_search_paths')
->willReturn(['/wrong']);
$finder = new BinaryFinder($this->cacheFactory, $config);
$this->assertFalse($finder->findBinaryPath('cat'));
$this->assertFalse($this->cache->get('cat'));
}
}