Signed-off-by: Michael Weimann <mail@michael-weimann.eu>tags/v14.0.0RC1
}, | }, | ||||
updateRow: function($tr, fileInfo, options) { | updateRow: function($tr, fileInfo, options) { | ||||
if(!fileInfo instanceof OCA.Sharing.SharedFileInfo) { | |||||
// recycle SharedFileInfo values if something tries to overwrite it | |||||
var oldModel = this.getModelForFile($tr); | |||||
if(_.isUndefined(fileInfo.recipientData) && oldModel.recipientData) { | |||||
fileInfo.recipientData = oldModel.recipientData; | |||||
} | |||||
if(_.isUndefined(fileInfo.recipients) && oldModel.recipientData) { | |||||
fileInfo.recipientData = oldModel.recipientData; | |||||
} | |||||
if(_.isUndefined(fileInfo.shares) && oldModel.shares) { | |||||
fileInfo.shares = oldModel.shares; | |||||
} | |||||
if(_.isUndefined(fileInfo.shareOwner) && oldModel.shareOwner) { | |||||
fileInfo.shareOwner = oldModel.shareOwner; | |||||
} | |||||
} | |||||
OCA.Files.FileList.prototype._createRow.updateRow(this, arguments); | |||||
// no-op, suppress re-rendering | |||||
}, | }, | ||||
reload: function() { | reload: function() { | ||||
* Converts the OCS API share response data to a file info | * Converts the OCS API share response data to a file info | ||||
* list | * list | ||||
* @param {Array} data OCS API share array | * @param {Array} data OCS API share array | ||||
* @param {bool} sharedWithUser | |||||
* @return {Array.<OCA.Sharing.SharedFileInfo>} array of shared file info | * @return {Array.<OCA.Sharing.SharedFileInfo>} array of shared file info | ||||
*/ | */ | ||||
_makeFilesFromShares: function(data, sharedWithUser) { | _makeFilesFromShares: function(data, sharedWithUser) { | ||||
/* jshint camelcase: false */ | /* jshint camelcase: false */ | ||||
var self = this; | |||||
var files = data; | var files = data; | ||||
if (this._linksOnly) { | if (this._linksOnly) { | ||||
// array of sorted names | // array of sorted names | ||||
data.mountType = 'shared'; | data.mountType = 'shared'; | ||||
delete data.recipientsCount; | delete data.recipientsCount; | ||||
if (self._sharedWithUser) { | |||||
// only for outgoing shres | |||||
if (sharedWithUser) { | |||||
// only for outgoing shares | |||||
delete data.shareTypes; | delete data.shareTypes; | ||||
} else { | } else { | ||||
data.shareTypes = _.keys(data.shareTypes); | data.shareTypes = _.keys(data.shareTypes); |
{ | { | ||||
"require-dev": { | "require-dev": { | ||||
"phpunit/phpunit": "~6.5", | "phpunit/phpunit": "~6.5", | ||||
"behat/behat": "~3.3.0", | |||||
"behat/behat": "~3.5.0", | |||||
"guzzlehttp/guzzle": "6.3.3", | "guzzlehttp/guzzle": "6.3.3", | ||||
"jarnaiz/behat-junit-formatter": "^1.3", | "jarnaiz/behat-junit-formatter": "^1.3", | ||||
"sabre/dav": "3.2.2" | "sabre/dav": "3.2.2" |
type: OC.SetupChecks.MESSAGE_TYPE_WARNING | type: OC.SetupChecks.MESSAGE_TYPE_WARNING | ||||
}) | }) | ||||
} | } | ||||
if(data.appDirsWithDifferentOwner && data.appDirsWithDifferentOwner.length > 0) { | |||||
var appDirsWithDifferentOwner = data.appDirsWithDifferentOwner.reduce( | |||||
function(appDirsWithDifferentOwner, directory) { | |||||
return appDirsWithDifferentOwner + '<li>' + directory + '</li>'; | |||||
}, | |||||
'' | |||||
); | |||||
messages.push({ | |||||
msg: t('core', 'Some app directories are owned by a different user than the web server one. ' + | |||||
'This may be the case if apps have been installed manually. ' + | |||||
'Check the permissions of the following app directories:') | |||||
+ '<ul>' + appDirsWithDifferentOwner + '</ul>', | |||||
type: OC.SetupChecks.MESSAGE_TYPE_WARNING | |||||
}); | |||||
} | |||||
} else { | } else { | ||||
messages.push({ | messages.push({ | ||||
msg: t('core', 'Error occurred while checking server setup'), | msg: t('core', 'Error occurred while checking server setup'), |
return {}; | return {}; | ||||
} | } | ||||
var permissions = this.get('possiblePermissions'); | |||||
var permissions = this.fileInfoModel.get('permissions'); | |||||
if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) { | if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) { | ||||
permissions = permissions & data.reshare.permissions; | permissions = permissions & data.reshare.permissions; | ||||
} | } |
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
}); | }); | ||||
}); | }); | ||||
it('should return a warning if there are app directories with wrong permissions', function(done) { | |||||
var async = OC.SetupChecks.checkSetup(); | |||||
suite.server.requests[0].respond( | |||||
200, | |||||
{ | |||||
'Content-Type': 'application/json', | |||||
}, | |||||
JSON.stringify({ | |||||
hasFileinfoInstalled: true, | |||||
isGetenvServerWorking: true, | |||||
isReadOnlyConfig: false, | |||||
hasWorkingFileLocking: true, | |||||
hasValidTransactionIsolationLevel: true, | |||||
suggestedOverwriteCliURL: '', | |||||
isUrandomAvailable: true, | |||||
securityDocs: 'https://docs.owncloud.org/myDocs.html', | |||||
serverHasInternetConnection: true, | |||||
isMemcacheConfigured: true, | |||||
forwardedForHeadersWorking: true, | |||||
isCorrectMemcachedPHPModuleInstalled: true, | |||||
hasPassedCodeIntegrityCheck: true, | |||||
isOpcacheProperlySetup: true, | |||||
hasOpcacheLoaded: true, | |||||
isSettimelimitAvailable: true, | |||||
hasFreeTypeSupport: true, | |||||
missingIndexes: [], | |||||
outdatedCaches: [], | |||||
cronErrors: [], | |||||
cronInfo: { | |||||
diffInSeconds: 0 | |||||
}, | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [ | |||||
'/some/path' | |||||
] | |||||
}) | |||||
); | |||||
async.done(function( data, s, x ){ | |||||
expect(data).toEqual([{ | |||||
msg: 'Some app directories are owned by a different user than the web server one. This may be the case if apps have been installed manually. Check the permissions of the following app directories:<ul><li>/some/path</li></ul>', | |||||
type: OC.SetupChecks.MESSAGE_TYPE_WARNING | |||||
}]); | |||||
done(); | |||||
}); | |||||
}); | |||||
it('should return an error if the forwarded for headers are not working', function(done) { | it('should return an error if the forwarded for headers are not working', function(done) { | ||||
var async = OC.SetupChecks.checkSetup(); | var async = OC.SetupChecks.checkSetup(); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
appDirsWithDifferentOwner: [], | |||||
isMemoryLimitSufficient: false | isMemoryLimitSufficient: false | ||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
cronInfo: { | cronInfo: { | ||||
diffInSeconds: 0 | diffInSeconds: 0 | ||||
}, | }, | ||||
isMemoryLimitSufficient: true | |||||
isMemoryLimitSufficient: true, | |||||
appDirsWithDifferentOwner: [] | |||||
}) | }) | ||||
); | ); | ||||
}])); | }])); | ||||
fetchSharesDeferred.resolve(makeOcsResponse([])); | fetchSharesDeferred.resolve(makeOcsResponse([])); | ||||
model.set('possiblePermissions', OC.PERMISSION_READ); | |||||
model.fileInfoModel.set('permissions', OC.PERMISSION_READ); | |||||
model.fetch(); | model.fetch(); | ||||
// no resharing allowed | // no resharing allowed |
<?php | <?php | ||||
declare(strict_types=1); | |||||
/** | |||||
* @copyright Copyright (c) 2018, Michael Weimann (<mail@michael-weimann.eu>) | |||||
* | |||||
* @license GNU AGPL version 3 or any later version | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Affero General Public License as | |||||
* published by the Free Software Foundation, either version 3 of the | |||||
* License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Affero General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Affero General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
namespace OC; | namespace OC; | ||||
namespace OC\Settings\Controller; | namespace OC\Settings\Controller; | ||||
use bantu\IniGetWrapper\IniGetWrapper; | use bantu\IniGetWrapper\IniGetWrapper; | ||||
use DirectoryIterator; | |||||
use Doctrine\DBAL\DBALException; | use Doctrine\DBAL\DBALException; | ||||
use Doctrine\DBAL\Platforms\SqlitePlatform; | use Doctrine\DBAL\Platforms\SqlitePlatform; | ||||
use GuzzleHttp\Exception\ClientException; | use GuzzleHttp\Exception\ClientException; | ||||
use OC; | |||||
use OC\AppFramework\Http; | use OC\AppFramework\Http; | ||||
use OC\DB\Connection; | use OC\DB\Connection; | ||||
use OC\DB\MissingIndexInformation; | use OC\DB\MissingIndexInformation; | ||||
return function_exists('opcache_get_status'); | return function_exists('opcache_get_status'); | ||||
} | } | ||||
/** | |||||
* Iterates through the configured app roots and | |||||
* tests if the subdirectories are owned by the same user than the current user. | |||||
* | |||||
* @return array | |||||
*/ | |||||
protected function getAppDirsWithDifferentOwner(): array { | |||||
$currentUser = posix_getpwuid(posix_getuid()); | |||||
$appDirsWithDifferentOwner = []; | |||||
foreach (OC::$APPSROOTS as $appRoot) { | |||||
if ($appRoot['writable'] === true) { | |||||
$appDirsWithDifferentOwner = array_merge( | |||||
$appDirsWithDifferentOwner, | |||||
$this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot) | |||||
); | |||||
} | |||||
} | |||||
sort($appDirsWithDifferentOwner); | |||||
return $appDirsWithDifferentOwner; | |||||
} | |||||
/** | |||||
* Tests if the directories for one apps directory are writable by the current user. | |||||
* | |||||
* @param array $currentUser The current user | |||||
* @param array $appRoot The app root config | |||||
* @return string[] The none writable directory paths inside the app root | |||||
*/ | |||||
private function getAppDirsWithDifferentOwnerForAppRoot(array $currentUser, array $appRoot): array { | |||||
$appDirsWithDifferentOwner = []; | |||||
$appsPath = $appRoot['path']; | |||||
$appsDir = new DirectoryIterator($appRoot['path']); | |||||
foreach ($appsDir as $fileInfo) { | |||||
if ($fileInfo->isDir() && !$fileInfo->isDot()) { | |||||
$absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename(); | |||||
$appDirUser = posix_getpwuid(fileowner($absAppPath)); | |||||
if ($appDirUser !== $currentUser) { | |||||
$appDirsWithDifferentOwner[] = $absAppPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename(); | |||||
} | |||||
} | |||||
} | |||||
return $appDirsWithDifferentOwner; | |||||
} | |||||
/** | /** | ||||
* @return DataResponse | * @return DataResponse | ||||
*/ | */ | ||||
'isPhpMailerUsed' => $this->isPhpMailerUsed(), | 'isPhpMailerUsed' => $this->isPhpMailerUsed(), | ||||
'mailSettingsDocumentation' => $this->urlGenerator->getAbsoluteURL('index.php/settings/admin'), | 'mailSettingsDocumentation' => $this->urlGenerator->getAbsoluteURL('index.php/settings/admin'), | ||||
'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(), | 'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(), | ||||
'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(), | |||||
] | ] | ||||
); | ); | ||||
} | } |
namespace Tests\Settings\Controller; | namespace Tests\Settings\Controller; | ||||
use OC; | |||||
use OC\DB\Connection; | use OC\DB\Connection; | ||||
use OC\MemoryInfo; | use OC\MemoryInfo; | ||||
use OC\Settings\Controller\CheckSetupController; | use OC\Settings\Controller\CheckSetupController; | ||||
/** | /** | ||||
* Class CheckSetupControllerTest | * Class CheckSetupControllerTest | ||||
* | * | ||||
* @backupStaticAttributes | |||||
* @package Tests\Settings\Controller | * @package Tests\Settings\Controller | ||||
*/ | */ | ||||
class CheckSetupControllerTest extends TestCase { | class CheckSetupControllerTest extends TestCase { | ||||
/** @var MemoryInfo|MockObject */ | /** @var MemoryInfo|MockObject */ | ||||
private $memoryInfo; | private $memoryInfo; | ||||
/** | |||||
* Holds a list of directories created during tests. | |||||
* | |||||
* @var array | |||||
*/ | |||||
private $dirsToRemove = []; | |||||
public function setUp() { | public function setUp() { | ||||
parent::setUp(); | parent::setUp(); | ||||
'isSqliteUsed', | 'isSqliteUsed', | ||||
'isPhpMailerUsed', | 'isPhpMailerUsed', | ||||
'hasOpcacheLoaded', | 'hasOpcacheLoaded', | ||||
'getAppDirsWithDifferentOwner', | |||||
])->getMock(); | ])->getMock(); | ||||
} | } | ||||
/** | |||||
* Removes directories created during tests. | |||||
* | |||||
* @after | |||||
* @return void | |||||
*/ | |||||
public function removeTestDirectories() { | |||||
foreach ($this->dirsToRemove as $dirToRemove) { | |||||
rmdir($dirToRemove); | |||||
} | |||||
$this->dirsToRemove = []; | |||||
} | |||||
public function testIsInternetConnectionWorkingDisabledViaConfig() { | public function testIsInternetConnectionWorkingDisabledViaConfig() { | ||||
$this->config->expects($this->once()) | $this->config->expects($this->once()) | ||||
->method('getSystemValue') | ->method('getSystemValue') | ||||
->method('isMemoryLimitSufficient') | ->method('isMemoryLimitSufficient') | ||||
->willReturn(true); | ->willReturn(true); | ||||
$this->checkSetupController | |||||
->expects($this->once()) | |||||
->method('getAppDirsWithDifferentOwner') | |||||
->willReturn([]); | |||||
$expected = new DataResponse( | $expected = new DataResponse( | ||||
[ | [ | ||||
'isGetenvServerWorking' => true, | 'isGetenvServerWorking' => true, | ||||
'isPhpMailerUsed' => false, | 'isPhpMailerUsed' => false, | ||||
'mailSettingsDocumentation' => 'https://server/index.php/settings/admin', | 'mailSettingsDocumentation' => 'https://server/index.php/settings/admin', | ||||
'isMemoryLimitSufficient' => true, | 'isMemoryLimitSufficient' => true, | ||||
'appDirsWithDifferentOwner' => [], | |||||
] | ] | ||||
); | ); | ||||
$this->assertEquals($expected, $this->checkSetupController->check()); | $this->assertEquals($expected, $this->checkSetupController->check()); | ||||
$this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); | $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); | ||||
} | } | ||||
/** | |||||
* Setups a temp directory and some subdirectories. | |||||
* Then calls the 'getAppDirsWithDifferentOwner' method. | |||||
* The result is expected to be empty since | |||||
* there are no directories with different owners than the current user. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function testAppDirectoryOwnersOk() { | |||||
$tempDir = tempnam(sys_get_temp_dir(), 'apps') . 'dir'; | |||||
mkdir($tempDir); | |||||
mkdir($tempDir . DIRECTORY_SEPARATOR . 'app1'); | |||||
mkdir($tempDir . DIRECTORY_SEPARATOR . 'app2'); | |||||
$this->dirsToRemove[] = $tempDir . DIRECTORY_SEPARATOR . 'app1'; | |||||
$this->dirsToRemove[] = $tempDir . DIRECTORY_SEPARATOR . 'app2'; | |||||
$this->dirsToRemove[] = $tempDir; | |||||
OC::$APPSROOTS = [ | |||||
[ | |||||
'path' => $tempDir, | |||||
'url' => '/apps', | |||||
'writable' => true, | |||||
], | |||||
]; | |||||
$this->assertSame( | |||||
[], | |||||
$this->invokePrivate($this->checkSetupController, 'getAppDirsWithDifferentOwner') | |||||
); | |||||
} | |||||
/** | |||||
* Calls the check for a none existing app root that is marked as not writable. | |||||
* It's expected that no error happens since the check shouldn't apply. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function testAppDirectoryOwnersNotWritable() { | |||||
$tempDir = tempnam(sys_get_temp_dir(), 'apps') . 'dir'; | |||||
OC::$APPSROOTS = [ | |||||
[ | |||||
'path' => $tempDir, | |||||
'url' => '/apps', | |||||
'writable' => false, | |||||
], | |||||
]; | |||||
$this->assertSame( | |||||
[], | |||||
$this->invokePrivate($this->checkSetupController, 'getAppDirsWithDifferentOwner') | |||||
); | |||||
} | |||||
public function testIsBuggyNss400() { | public function testIsBuggyNss400() { | ||||
$this->config->expects($this->any()) | $this->config->expects($this->any()) | ||||
->method('getSystemValue') | ->method('getSystemValue') |
<?php | <?php | ||||
declare(strict_types=1); | |||||
/** | |||||
* @copyright Copyright (c) 2018, Michael Weimann (<mail@michael-weimann.eu>) | |||||
* | |||||
* @license GNU AGPL version 3 or any later version | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Affero General Public License as | |||||
* published by the Free Software Foundation, either version 3 of the | |||||
* License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Affero General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Affero General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
namespace Test; | namespace Test; | ||||