diff options
author | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2016-06-09 18:45:12 +0200 |
---|---|---|
committer | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2016-06-09 18:45:12 +0200 |
commit | a636e4ff28b25797d6cc7750bc1efe52437ec67f (patch) | |
tree | ef385b9ef924088b6d6c1404d659e6be450aaf1f | |
parent | 28193732ea24094335cccddf5fe03aeeeb6f5894 (diff) | |
parent | 6ba18934e6f095de08bec7bdc10c45485eeb5cc7 (diff) | |
download | nextcloud-server-a636e4ff28b25797d6cc7750bc1efe52437ec67f.tar.gz nextcloud-server-a636e4ff28b25797d6cc7750bc1efe52437ec67f.zip |
Downstream 2016-06-09
Merge branch 'master' of https://github.com/owncloud/core into downstream-160609
102 files changed, 1210 insertions, 270 deletions
diff --git a/.htaccess b/.htaccess index 5bf7b321f0c..bd9f5af3f70 100644 --- a/.htaccess +++ b/.htaccess @@ -56,9 +56,9 @@ RewriteRule ^\.well-known/carddav /remote.php/dav/ [R=301,L] RewriteRule ^\.well-known/caldav /remote.php/dav/ [R=301,L] RewriteRule ^remote/(.*) remote.php [QSA,L] - RewriteRule ^(build|tests|config|lib|3rdparty|templates)/.* - [R=404,L] + RewriteRule ^(?:build|tests|config|lib|3rdparty|templates)/.* - [R=404,L] RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/.* - RewriteRule ^(\.|autotest|occ|issue|indie|db_|console).* - [R=404,L] + RewriteRule ^(?:\.|autotest|occ|issue|indie|db_|console).* - [R=404,L] </IfModule> <IfModule mod_mime.c> AddType image/svg+xml svg svgz diff --git a/apps/dav/lib/Connector/Sabre/Auth.php b/apps/dav/lib/Connector/Sabre/Auth.php index 7b959a0d899..27900cc1cad 100644 --- a/apps/dav/lib/Connector/Sabre/Auth.php +++ b/apps/dav/lib/Connector/Sabre/Auth.php @@ -110,10 +110,10 @@ class Auth extends AbstractBasic { $this->session->close(); return true; } else { - \OC_Util::setUpFS(); //login hooks may need early access to the filesystem + \OC_Util::setupFS(); //login hooks may need early access to the filesystem if($this->userSession->logClientIn($username, $password)) { $this->userSession->createSessionToken($this->request, $this->userSession->getUser()->getUID(), $username, $password); - \OC_Util::setUpFS($this->userSession->getUser()->getUID()); + \OC_Util::setupFS($this->userSession->getUser()->getUID()); $this->session->set(self::DAV_AUTHENTICATED, $this->userSession->getUser()->getUID()); $this->session->close(); return true; diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php index e7226b58196..ddab34605f3 100644 --- a/apps/dav/lib/Connector/Sabre/Directory.php +++ b/apps/dav/lib/Connector/Sabre/Directory.php @@ -196,6 +196,8 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage()); } catch (\OCP\Files\InvalidPathException $ex) { throw new InvalidPath($ex->getMessage()); + } catch (ForbiddenException $e) { + throw new \Sabre\DAV\Exception\Forbidden(); } } diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index a4e13709559..dc47416cca8 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -168,20 +168,19 @@ class FilesPlugin extends ServerPlugin { */ function checkMove($source, $destination) { $sourceNode = $this->tree->getNodeForPath($source); - if ($sourceNode instanceof FutureFile) { + if (!$sourceNode instanceof Node) { return; } list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($source); list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination); if ($sourceDir !== $destinationDir) { - $sourceFileInfo = $this->fileView->getFileInfo($source); - - if ($sourceFileInfo === false) { + $sourceNodeFileInfo = $sourceNode->getFileInfo(); + if (is_null($sourceNodeFileInfo)) { throw new NotFound($source . ' does not exist'); } - if (!$sourceFileInfo->isDeletable()) { + if (!$sourceNodeFileInfo->isDeletable()) { throw new Forbidden($source . " cannot be deleted"); } } diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php index 04640bbde4f..c41b5137fde 100644 --- a/apps/dav/lib/Connector/Sabre/Node.php +++ b/apps/dav/lib/Connector/Sabre/Node.php @@ -347,4 +347,8 @@ abstract class Node implements \Sabre\DAV\INode { public function changeLock($type) { $this->fileView->changeLock($this->path, $type); } + + public function getFileInfo() { + return $this->info; + } } diff --git a/apps/dav/lib/Connector/Sabre/ObjectTree.php b/apps/dav/lib/Connector/Sabre/ObjectTree.php index 599f3fdfd0e..9e7d876187d 100644 --- a/apps/dav/lib/Connector/Sabre/ObjectTree.php +++ b/apps/dav/lib/Connector/Sabre/ObjectTree.php @@ -161,6 +161,8 @@ class ObjectTree extends \Sabre\DAV\Tree { throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid'); } catch (LockedException $e) { throw new \Sabre\DAV\Exception\Locked(); + } catch (ForbiddenException $e) { + throw new \Sabre\DAV\Exception\Forbidden(); } } diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php index 87fec17bf6b..80f284e470e 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php @@ -24,6 +24,7 @@ */ namespace OCA\DAV\Tests\unit\Connector\Sabre; +use OCA\DAV\Connector\Sabre\FilesPlugin; use OCP\Files\StorageNotAvailableException; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; @@ -36,16 +37,16 @@ use Test\TestCase; * See the COPYING-README file. */ class FilesPluginTest extends TestCase { - const GETETAG_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::GETETAG_PROPERTYNAME; - const FILEID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::FILEID_PROPERTYNAME; - const INTERNAL_FILEID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::INTERNAL_FILEID_PROPERTYNAME; - const SIZE_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::SIZE_PROPERTYNAME; - const PERMISSIONS_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::PERMISSIONS_PROPERTYNAME; - const LASTMODIFIED_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::LASTMODIFIED_PROPERTYNAME; - const DOWNLOADURL_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DOWNLOADURL_PROPERTYNAME; - const OWNER_ID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_ID_PROPERTYNAME; - const OWNER_DISPLAY_NAME_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME; - const DATA_FINGERPRINT_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME; + const GETETAG_PROPERTYNAME = FilesPlugin::GETETAG_PROPERTYNAME; + const FILEID_PROPERTYNAME = FilesPlugin::FILEID_PROPERTYNAME; + const INTERNAL_FILEID_PROPERTYNAME = FilesPlugin::INTERNAL_FILEID_PROPERTYNAME; + const SIZE_PROPERTYNAME = FilesPlugin::SIZE_PROPERTYNAME; + const PERMISSIONS_PROPERTYNAME = FilesPlugin::PERMISSIONS_PROPERTYNAME; + const LASTMODIFIED_PROPERTYNAME = FilesPlugin::LASTMODIFIED_PROPERTYNAME; + const DOWNLOADURL_PROPERTYNAME = FilesPlugin::DOWNLOADURL_PROPERTYNAME; + const OWNER_ID_PROPERTYNAME = FilesPlugin::OWNER_ID_PROPERTYNAME; + const OWNER_DISPLAY_NAME_PROPERTYNAME = FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME; + const DATA_FINGERPRINT_PROPERTYNAME = FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME; /** * @var \Sabre\DAV\Server | \PHPUnit_Framework_MockObject_MockObject @@ -58,7 +59,7 @@ class FilesPluginTest extends TestCase { private $tree; /** - * @var \OCA\DAV\Connector\Sabre\FilesPlugin + * @var FilesPlugin */ private $plugin; @@ -84,11 +85,11 @@ class FilesPluginTest extends TestCase { ->disableOriginalConstructor() ->getMock(); $this->config = $this->getMock('\OCP\IConfig'); - $this->config->method('getSystemValue') + $this->config->expects($this->any())->method('getSystemValue') ->with($this->equalTo('data-fingerprint'), $this->equalTo('')) ->willReturn('my_fingerprint'); - $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->plugin = new FilesPlugin( $this->tree, $this->view, $this->config @@ -263,7 +264,7 @@ class FilesPluginTest extends TestCase { } public function testGetPublicPermissions() { - $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->plugin = new FilesPlugin( $this->tree, $this->view, $this->config, @@ -331,7 +332,7 @@ class FilesPluginTest extends TestCase { $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') ->disableOriginalConstructor() ->getMock(); - $node->method('getPath')->willReturn('/'); + $node->expects($this->any())->method('getPath')->willReturn('/'); $propFind = new PropFind( '/', @@ -432,11 +433,16 @@ class FilesPluginTest extends TestCase { ->method('isDeletable') ->willReturn(false); - $this->view->expects($this->once()) + $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node') + ->disableOriginalConstructor() + ->getMock(); + $node->expects($this->once()) ->method('getFileInfo') - ->with('FolderA/test.txt') ->willReturn($fileInfoFolderATestTXT); + $this->tree->expects($this->once())->method('getNodeForPath') + ->willReturn($node); + $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } @@ -448,11 +454,16 @@ class FilesPluginTest extends TestCase { ->method('isDeletable') ->willReturn(true); - $this->view->expects($this->once()) + $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node') + ->disableOriginalConstructor() + ->getMock(); + $node->expects($this->once()) ->method('getFileInfo') - ->with('FolderA/test.txt') ->willReturn($fileInfoFolderATestTXT); + $this->tree->expects($this->once())->method('getNodeForPath') + ->willReturn($node); + $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } @@ -461,10 +472,15 @@ class FilesPluginTest extends TestCase { * @expectedExceptionMessage FolderA/test.txt does not exist */ public function testMoveSrcNotExist() { - $this->view->expects($this->once()) + $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node') + ->disableOriginalConstructor() + ->getMock(); + $node->expects($this->once()) ->method('getFileInfo') - ->with('FolderA/test.txt') - ->willReturn(false); + ->willReturn(null); + + $this->tree->expects($this->once())->method('getNodeForPath') + ->willReturn($node); $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php new file mode 100644 index 00000000000..4f481cba557 --- /dev/null +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php @@ -0,0 +1,59 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Robin Appelman <icewind@owncloud.com> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\DAV\Tests\unit\Connector\Sabre\RequestTest; + +use OC\Connector\Sabre\Exception\FileLocked; +use OCP\AppFramework\Http; +use OCP\Lock\ILockingProvider; + +/** + * Class DeleteTest + * + * @group DB + * + * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest + */ +class DeleteTest extends RequestTest { + public function testBasicUpload() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $view->file_put_contents('foo.txt', 'asd'); + $mount = $view->getMount('foo.txt'); + $internalPath = $view->getAbsolutePath(); + + // create a ghost file + $mount->getStorage()->unlink($mount->getInternalPath($internalPath)); + + // cache entry still exists + $this->assertInstanceOf('\OCP\Files\FileInfo', $view->getFileInfo('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'DELETE', '/foo.txt'); + + $this->assertEquals(Http::STATUS_NO_CONTENT, $response->getStatus()); + + // no longer in the cache + $this->assertFalse($view->getFileInfo('foo.txt')); + } +} diff --git a/apps/federatedfilesharing/l10n/ar.js b/apps/federatedfilesharing/l10n/ar.js new file mode 100644 index 00000000000..451663a7b6b --- /dev/null +++ b/apps/federatedfilesharing/l10n/ar.js @@ -0,0 +1,6 @@ +OC.L10N.register( + "federatedfilesharing", + { + "Invalid Federated Cloud ID" : "معرّف السحابة المتحدة غير صالح" +}, +"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"); diff --git a/apps/federatedfilesharing/l10n/ar.json b/apps/federatedfilesharing/l10n/ar.json new file mode 100644 index 00000000000..630ab95f64b --- /dev/null +++ b/apps/federatedfilesharing/l10n/ar.json @@ -0,0 +1,4 @@ +{ "translations": { + "Invalid Federated Cloud ID" : "معرّف السحابة المتحدة غير صالح" +},"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;" +}
\ No newline at end of file diff --git a/apps/federatedfilesharing/l10n/pl.js b/apps/federatedfilesharing/l10n/pl.js index 9bd5edd9b56..1d8b949c74a 100644 --- a/apps/federatedfilesharing/l10n/pl.js +++ b/apps/federatedfilesharing/l10n/pl.js @@ -2,6 +2,7 @@ OC.L10N.register( "federatedfilesharing", { "Sharing %s failed, because this item is already shared with %s" : "Współdzielenie %s nie powiodło się, ponieważ element jest już współdzielony z %s", + "File is already shared with %s" : "Plik jest już współdzielony z %s", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Współdzielenie %s nie powiodło się, nie można odnaleźć %s. Prawdopobnie serwer nie jest teraz osiągalny.", "Accept" : "Akceptuj", "Open documentation" : "Otwórz dokumentację", diff --git a/apps/federatedfilesharing/l10n/pl.json b/apps/federatedfilesharing/l10n/pl.json index 5fd8fb130b7..c44eecbeeb7 100644 --- a/apps/federatedfilesharing/l10n/pl.json +++ b/apps/federatedfilesharing/l10n/pl.json @@ -1,5 +1,6 @@ { "translations": { "Sharing %s failed, because this item is already shared with %s" : "Współdzielenie %s nie powiodło się, ponieważ element jest już współdzielony z %s", + "File is already shared with %s" : "Plik jest już współdzielony z %s", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Współdzielenie %s nie powiodło się, nie można odnaleźć %s. Prawdopobnie serwer nie jest teraz osiągalny.", "Accept" : "Akceptuj", "Open documentation" : "Otwórz dokumentację", diff --git a/apps/federation/css/settings-admin.css b/apps/federation/css/settings-admin.css index 55b1dd64d15..150412c156f 100644 --- a/apps/federation/css/settings-admin.css +++ b/apps/federation/css/settings-admin.css @@ -17,10 +17,13 @@ cursor: pointer; } -#listOfTrustedServers li:hover { - cursor: pointer; -} - #listOfTrustedServers .status { margin-right: 10px; } + +#listOfTrustedServers .icon { + cursor: pointer; + display: inline-block; + vertical-align: middle; + margin-left: 10px; +} diff --git a/apps/federation/js/settings-admin.js b/apps/federation/js/settings-admin.js index 7d531b39d8c..45d5d62a5a3 100644 --- a/apps/federation/js/settings-admin.js +++ b/apps/federation/js/settings-admin.js @@ -42,8 +42,9 @@ $(document).ready(function () { $('ul#listOfTrustedServers').prepend( $('<li>') .attr('id', data.id) - .attr('class', 'icon-delete') - .html('<span class="status indeterminate"></span>' + data.url) + .html('<span class="status indeterminate"></span>' + + data.url + + '<span class="icon icon-delete"></span>') ); OC.msg.finishedSuccess('#ocFederationAddServer .msg', data.message); }) @@ -56,10 +57,10 @@ $(document).ready(function () { } }); - // remove trusted server from list - $( "#listOfTrustedServers" ).on('click', 'li', function() { - var id = $(this).attr('id'); - var $this = $(this); +// remove trusted server from list + $( "#listOfTrustedServers" ).on('click', 'li > .icon-delete', function() { + var $this = $(this).parent(); + id = $this.attr('id'); $.ajax({ url: OC.generateUrl('/apps/federation/trusted-servers/' + id), type: 'DELETE', diff --git a/apps/federation/templates/settings-admin.php b/apps/federation/templates/settings-admin.php index 704fc9a9ace..ce66214de7c 100644 --- a/apps/federation/templates/settings-admin.php +++ b/apps/federation/templates/settings-admin.php @@ -23,7 +23,7 @@ style('federation', 'settings-admin') </p> <ul id="listOfTrustedServers"> <?php foreach($_['trustedServers'] as $trustedServer) { ?> - <li id="<?php p($trustedServer['id']); ?>" class="icon-delete"> + <li id="<?php p($trustedServer['id']); ?>"> <?php if((int)$trustedServer['status'] === TrustedServers::STATUS_OK) { ?> <span class="status success"></span> <?php @@ -36,6 +36,7 @@ style('federation', 'settings-admin') <span class="status error"></span> <?php } ?> <?php p($trustedServer['url']); ?> + <span class="icon icon-delete"></span> </li> <?php } ?> </ul> diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 7a3d78f9663..fbfa510e07e 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -53,6 +53,9 @@ this.$showHiddenFiles = $('input#showhiddenfilesToggle'); var showHidden = $('#showHiddenFiles').val() === "1"; this.$showHiddenFiles.prop('checked', showHidden); + if ($('#fileNotFound').val() === "1") { + OC.Notification.showTemporary(t('files', 'File could not be found')); + } this._filesConfig = new OC.Backbone.Model({ showhidden: showHidden diff --git a/apps/files/l10n/da.js b/apps/files/l10n/da.js index e1013bcafbb..e585ccacc1c 100644 --- a/apps/files/l10n/da.js +++ b/apps/files/l10n/da.js @@ -32,6 +32,8 @@ OC.L10N.register( "Could not get result from server." : "Kunne ikke hente resultat fra server.", "Uploading..." : "Uploader...", "..." : "...", + "Any moment now..." : "Når som helst...", + "Soon..." : "Snart...", "File upload is in progress. Leaving the page now will cancel the upload." : "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.", "Actions" : "Handlinger", "Download" : "Download", @@ -47,6 +49,8 @@ OC.L10N.register( "This directory is unavailable, please check the logs or contact the administrator" : "Denne mappe er utilgængelig, tjek venligst loggene eller kontakt administratoren", "Could not move \"{file}\", target exists" : "Kunne ikke flytte \"{file}\" - der findes allerede en fil med dette navn", "Could not move \"{file}\"" : "Kunne ikke flytte \"{file}\"", + "{newName} already exists" : "{newName} eksistere allerede", + "Error deleting file \"{fileName}\"." : "Fejl under sletning af filen \"{fileName}\"", "No entries in this folder match '{filter}'" : "Der er ingen poster i denne mappe, der matcher '{filter}'", "Name" : "Navn", "Size" : "Størrelse", @@ -68,6 +72,7 @@ OC.L10N.register( "_%n byte_::_%n bytes_" : ["%n byte","%n bytes"], "Favorited" : "Gjort til foretrukken", "Favorite" : "Foretrukken", + "Local link" : "Lokalt link", "Folder" : "Mappe", "New folder" : "Ny Mappe", "{newname} already exists" : "{newname} eksistere allerede", @@ -97,6 +102,7 @@ OC.L10N.register( "Save" : "Gem", "Missing permissions to edit from here." : "Rettighed mangler til at redigere på dette sted", "Settings" : "Indstillinger", + "Show hidden files" : "Vis skjulte filer", "WebDAV" : "WebDAV", "No files in here" : "Her er ingen filer", "Upload some content or sync with your devices!" : "Overfør indhold eller synkronisér med dine enheder!", diff --git a/apps/files/l10n/da.json b/apps/files/l10n/da.json index 3162770cc40..499533d77ba 100644 --- a/apps/files/l10n/da.json +++ b/apps/files/l10n/da.json @@ -30,6 +30,8 @@ "Could not get result from server." : "Kunne ikke hente resultat fra server.", "Uploading..." : "Uploader...", "..." : "...", + "Any moment now..." : "Når som helst...", + "Soon..." : "Snart...", "File upload is in progress. Leaving the page now will cancel the upload." : "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.", "Actions" : "Handlinger", "Download" : "Download", @@ -45,6 +47,8 @@ "This directory is unavailable, please check the logs or contact the administrator" : "Denne mappe er utilgængelig, tjek venligst loggene eller kontakt administratoren", "Could not move \"{file}\", target exists" : "Kunne ikke flytte \"{file}\" - der findes allerede en fil med dette navn", "Could not move \"{file}\"" : "Kunne ikke flytte \"{file}\"", + "{newName} already exists" : "{newName} eksistere allerede", + "Error deleting file \"{fileName}\"." : "Fejl under sletning af filen \"{fileName}\"", "No entries in this folder match '{filter}'" : "Der er ingen poster i denne mappe, der matcher '{filter}'", "Name" : "Navn", "Size" : "Størrelse", @@ -66,6 +70,7 @@ "_%n byte_::_%n bytes_" : ["%n byte","%n bytes"], "Favorited" : "Gjort til foretrukken", "Favorite" : "Foretrukken", + "Local link" : "Lokalt link", "Folder" : "Mappe", "New folder" : "Ny Mappe", "{newname} already exists" : "{newname} eksistere allerede", @@ -95,6 +100,7 @@ "Save" : "Gem", "Missing permissions to edit from here." : "Rettighed mangler til at redigere på dette sted", "Settings" : "Indstillinger", + "Show hidden files" : "Vis skjulte filer", "WebDAV" : "WebDAV", "No files in here" : "Her er ingen filer", "Upload some content or sync with your devices!" : "Overfør indhold eller synkronisér med dine enheder!", diff --git a/apps/files/l10n/pl.js b/apps/files/l10n/pl.js index 01259a538ab..cb243f6bf4d 100644 --- a/apps/files/l10n/pl.js +++ b/apps/files/l10n/pl.js @@ -45,6 +45,7 @@ OC.L10N.register( "Unable to determine date" : "Nie można ustalić daty", "This operation is forbidden" : "Ta operacja jest niedozwolona", "This directory is unavailable, please check the logs or contact the administrator" : "Ten folder jest niedostępny, proszę sprawdzić logi lub skontaktować się z administratorem.", + "Could not move \"{file}\", target exists" : "Nie można było przenieść „{file}” – plik o takiej nazwie już istnieje", "Could not move \"{file}\"" : "Nie można było przenieść \"{file}\"", "Could not create file \"{file}\"" : "Nie można było utworzyć pliku \"{file}\"", "Could not create file \"{file}\" because it already exists" : "Nie można było utworzyć pliku \"{file}\", ponieważ ten plik już istnieje.", diff --git a/apps/files/l10n/pl.json b/apps/files/l10n/pl.json index fd4d5116953..60323d0e757 100644 --- a/apps/files/l10n/pl.json +++ b/apps/files/l10n/pl.json @@ -43,6 +43,7 @@ "Unable to determine date" : "Nie można ustalić daty", "This operation is forbidden" : "Ta operacja jest niedozwolona", "This directory is unavailable, please check the logs or contact the administrator" : "Ten folder jest niedostępny, proszę sprawdzić logi lub skontaktować się z administratorem.", + "Could not move \"{file}\", target exists" : "Nie można było przenieść „{file}” – plik o takiej nazwie już istnieje", "Could not move \"{file}\"" : "Nie można było przenieść \"{file}\"", "Could not create file \"{file}\"" : "Nie można było utworzyć pliku \"{file}\"", "Could not create file \"{file}\" because it already exists" : "Nie można było utworzyć pliku \"{file}\", ponieważ ten plik już istnieje.", diff --git a/apps/files/lib/Controller/ViewController.php b/apps/files/lib/Controller/ViewController.php index 1b0903b41d3..18b6cf719c5 100644 --- a/apps/files/lib/Controller/ViewController.php +++ b/apps/files/lib/Controller/ViewController.php @@ -27,9 +27,9 @@ namespace OCA\Files\Controller; use OC\AppFramework\Http\Request; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\ContentSecurityPolicy; -use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IL10N; use OCP\INavigationManager; @@ -37,7 +37,6 @@ use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUserSession; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use OCP\AppFramework\Http\NotFoundResponse; use OCP\Files\Folder; use OCP\App\IAppManager; @@ -142,11 +141,15 @@ class ViewController extends Controller { * @param string $view * @param string $fileid * @return TemplateResponse - * @throws \OCP\Files\NotFoundException */ public function index($dir = '', $view = '', $fileid = null) { + $fileNotFound = false; if ($fileid !== null) { - return $this->showFile($fileid); + try { + return $this->showFile($fileid); + } catch (NotFoundException $e) { + $fileNotFound = true; + } } $nav = new \OCP\Template('files', 'appnavigation', ''); @@ -245,6 +248,7 @@ class ViewController extends Controller { $params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc'); $showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false); $params['showHiddenFiles'] = $showHidden ? 1 : 0; + $params['fileNotFound'] = $fileNotFound ? 1 : 0; $params['appNavigation'] = $nav; $params['appContents'] = $contentItems; $this->navigationManager->setActiveEntry('files_index'); @@ -265,40 +269,37 @@ class ViewController extends Controller { * Redirects to the file list and highlight the given file id * * @param string $fileId file id to show - * @return Response redirect response or not found response + * @return RedirectResponse redirect response or not found response + * @throws \OCP\Files\NotFoundException * * @NoCSRFRequired * @NoAdminRequired */ public function showFile($fileId) { - try { - $uid = $this->userSession->getUser()->getUID(); - $baseFolder = $this->rootFolder->get($uid . '/files/'); - $files = $baseFolder->getById($fileId); - $params = []; + $uid = $this->userSession->getUser()->getUID(); + $baseFolder = $this->rootFolder->get($uid . '/files/'); + $files = $baseFolder->getById($fileId); + $params = []; - if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) { - $baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/'); - $files = $baseFolder->getById($fileId); - $params['view'] = 'trashbin'; - } + if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) { + $baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/'); + $files = $baseFolder->getById($fileId); + $params['view'] = 'trashbin'; + } - if (!empty($files)) { - $file = current($files); - if ($file instanceof Folder) { - // set the full path to enter the folder - $params['dir'] = $baseFolder->getRelativePath($file->getPath()); - } else { - // set parent path as dir - $params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath()); - // and scroll to the entry - $params['scrollto'] = $file->getName(); - } - return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params)); + if (!empty($files)) { + $file = current($files); + if ($file instanceof Folder) { + // set the full path to enter the folder + $params['dir'] = $baseFolder->getRelativePath($file->getPath()); + } else { + // set parent path as dir + $params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath()); + // and scroll to the entry + $params['scrollto'] = $file->getName(); } - } catch (\OCP\Files\NotFoundException $e) { - return new NotFoundResponse(); + return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params)); } - return new NotFoundResponse(); + throw new \OCP\Files\NotFoundException(); } } diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 7281edd3aec..42ce941a4a5 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -14,6 +14,7 @@ <input type="hidden" name="usedSpacePercent" id="usedSpacePercent" value="<?php p($_['usedSpacePercent']); ?>" /> <input type="hidden" name="owner" id="owner" value="<?php p($_['owner']); ?>" /> <input type="hidden" name="ownerDisplayName" id="ownerDisplayName" value="<?php p($_['ownerDisplayName']); ?>" /> +<input type="hidden" name="fileNotFound" id="fileNotFound" value="<?php p($_['fileNotFound']); ?>"" /> <?php if (!$_['isPublic']) :?> <input type="hidden" name="mailNotificationEnabled" id="mailNotificationEnabled" value="<?php p($_['mailNotificationEnabled']) ?>" /> <input type="hidden" name="mailPublicNotificationEnabled" id="mailPublicNotificationEnabled" value="<?php p($_['mailPublicNotificationEnabled']) ?>" /> diff --git a/apps/files/tests/Controller/ViewControllerTest.php b/apps/files/tests/Controller/ViewControllerTest.php index 049f44fc0af..34c40ecea5c 100644 --- a/apps/files/tests/Controller/ViewControllerTest.php +++ b/apps/files/tests/Controller/ViewControllerTest.php @@ -26,6 +26,7 @@ namespace OCA\Files\Tests\Controller; use OCA\Files\Controller\ViewController; use OCP\AppFramework\Http; +use OCP\Files\NotFoundException; use OCP\IUser; use OCP\Template; use Test\TestCase; @@ -259,7 +260,8 @@ class ViewControllerTest extends TestCase { 'isPublic' => false, 'defaultFileSorting' => 'name', 'defaultFileSortingDirection' => 'asc', - 'showHiddenFiles' => false, + 'showHiddenFiles' => 0, + 'fileNotFound' => 0, 'mailNotificationEnabled' => 'no', 'mailPublicNotificationEnabled' => 'no', 'allowShareWithLink' => 'yes', @@ -410,11 +412,14 @@ class ViewControllerTest extends TestCase { ->with(123) ->will($this->returnValue([])); - $expected = new Http\NotFoundResponse(); if ($useShowFile) { - $this->assertEquals($expected, $this->viewController->showFile(123)); + $this->setExpectedException('OCP\Files\NotFoundException'); + $this->viewController->showFile(123); } else { - $this->assertEquals($expected, $this->viewController->index('/whatever', '', '123')); + $response = $this->viewController->index('MyDir', 'MyView', '123'); + $this->assertInstanceOf('OCP\AppFramework\Http\TemplateResponse', $response); + $params = $response->getParams(); + $this->assertEquals(1, $params['fileNotFound']); } } diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 8d2cb52d67c..2477f513db3 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -299,7 +299,8 @@ StorageConfig.prototype = { mountPoint: this.mountPoint, backend: this.backend, authMechanism: this.authMechanism, - backendOptions: this.backendOptions + backendOptions: this.backendOptions, + testOnly: true }; if (this.id) { data.id = this.id; @@ -326,6 +327,7 @@ StorageConfig.prototype = { $.ajax({ type: 'GET', url: OC.generateUrl(this._url + '/{id}', {id: this.id}), + data: {'testOnly': true}, success: options.success, error: options.error }); @@ -908,6 +910,7 @@ MountConfigListView.prototype = _.extend({ $.ajax({ type: 'GET', url: OC.generateUrl('apps/files_external/userglobalstorages'), + data: {'testOnly' : true}, contentType: 'application/json', success: function(result) { var onCompletion = jQuery.Deferred(); diff --git a/apps/files_external/js/statusmanager.js b/apps/files_external/js/statusmanager.js index 118ec17d246..91974f2d04d 100644 --- a/apps/files_external/js/statusmanager.js +++ b/apps/files_external/js/statusmanager.js @@ -78,6 +78,7 @@ OCA.External.StatusManager = { defObj = $.ajax({ type: 'GET', url: OC.webroot + '/index.php/apps/files_external/' + ((mountData.type === 'personal') ? 'userstorages' : 'userglobalstorages') + '/' + mountData.id, + data: {'testOnly' : false}, success: function (response) { if (response && response.status === 0) { self.mountStatus[mountData.mount_point] = response; diff --git a/apps/files_external/l10n/pl.js b/apps/files_external/l10n/pl.js index 0ba2646601b..8c3e394627e 100644 --- a/apps/files_external/l10n/pl.js +++ b/apps/files_external/l10n/pl.js @@ -2,6 +2,7 @@ OC.L10N.register( "files_external", { "Fetching access tokens failed. Verify that your app key and secret are correct." : "Otrzymano błędne żądanie tokenów. Sprawdź, czy klucz aplikacji oraz klucz poufny są poprawne.", + "Please provide a valid app key and secret." : "Proszę podać prawidłowy klucz aplikacji i klucz sekretny.", "Step 1 failed. Exception: %s" : "Krok 1 błędny. Błąd: %s", "Step 2 failed. Exception: %s" : "Krok 2 błędny. Błąd: %s", "External storage" : "Zewnętrzne zasoby dyskowe", diff --git a/apps/files_external/l10n/pl.json b/apps/files_external/l10n/pl.json index 8809813c9ba..8e00fc31863 100644 --- a/apps/files_external/l10n/pl.json +++ b/apps/files_external/l10n/pl.json @@ -1,5 +1,6 @@ { "translations": { "Fetching access tokens failed. Verify that your app key and secret are correct." : "Otrzymano błędne żądanie tokenów. Sprawdź, czy klucz aplikacji oraz klucz poufny są poprawne.", + "Please provide a valid app key and secret." : "Proszę podać prawidłowy klucz aplikacji i klucz sekretny.", "Step 1 failed. Exception: %s" : "Krok 1 błędny. Błąd: %s", "Step 2 failed. Exception: %s" : "Krok 2 błędny. Błąd: %s", "External storage" : "Zewnętrzne zasoby dyskowe", diff --git a/apps/files_external/l10n/pt_PT.js b/apps/files_external/l10n/pt_PT.js index 9d3998d903b..68f183a36b3 100644 --- a/apps/files_external/l10n/pt_PT.js +++ b/apps/files_external/l10n/pt_PT.js @@ -7,13 +7,13 @@ OC.L10N.register( "Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s", "Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s", "External storage" : "Armazenamento Externo", - "Dropbox App Configuration" : "Configuração da app Dropbox", - "Google Drive App Configuration" : "Configuração da app Google Drive", + "Dropbox App Configuration" : "Configuração da aplicação Dropbox", + "Google Drive App Configuration" : "Configuração da aplicação Google Drive", "Personal" : "Pessoal", "System" : "Sistema", "Grant access" : "Conceder acesso", - "Error configuring OAuth1" : "Erro de configuração OAuth1", - "Error configuring OAuth2" : "Erro de configuração OAuth2", + "Error configuring OAuth1" : "Erro ao configurar OAuth1", + "Error configuring OAuth2" : "Erro ao configurar OAuth2", "Generate keys" : "Gerar chaves", "Error generating key pair" : "Erro ao gerar chave par", "All users. Type to select user or group." : "Todos os utilizadores. Digite para selecionar o utilizador ou grupo.", @@ -27,14 +27,14 @@ OC.L10N.register( "Couldn't get the list of external mount points: {type}" : "Não foi possível conseguir a lista de pontos de montagem externos: {type}", "There was an error with message: " : "Houve um erro com a mensagem:", "External mount error" : "Erro de montagem externa", - "external-storage" : "Armazenamento Externo", + "external-storage" : "armazenamento externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível conseguir a lista de pontos de montagem Windows na rede: resposta vazia do servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor, clique na fila vermelha para mais informação", "Please enter the credentials for the {mount} mount" : "Por favor, introduza as credenciais para {mount}", "Username" : "Nome de utilizador", "Password" : "Palavra-passe", "Credentials saved" : "Credenciais guardadas", - "Credentials saving failed" : "Falha ao salvar credenciais", + "Credentials saving failed" : "Falha ao guardar as credenciais", "Credentials required" : "Credenciais necessárias", "Save" : "Guardar", "Storage with id \"%i\" not found" : "Não foi encontrado o armazenamento com a id. \"%i\"", @@ -49,7 +49,7 @@ OC.L10N.register( "Insufficient data: %s" : "Dados insuficientes: %s", "%s" : "%s", "Storage with id \"%i\" is not user editable" : "Armazenamento com id \"%i\" não é editável pelo utilizador", - "Access key" : "Código de acesso", + "Access key" : "Chave de acesso", "Secret key" : "Código secreto", "Builtin" : "Integrado", "None" : "Nenhum", diff --git a/apps/files_external/l10n/pt_PT.json b/apps/files_external/l10n/pt_PT.json index fab0f05a830..93922c67762 100644 --- a/apps/files_external/l10n/pt_PT.json +++ b/apps/files_external/l10n/pt_PT.json @@ -5,13 +5,13 @@ "Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s", "Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s", "External storage" : "Armazenamento Externo", - "Dropbox App Configuration" : "Configuração da app Dropbox", - "Google Drive App Configuration" : "Configuração da app Google Drive", + "Dropbox App Configuration" : "Configuração da aplicação Dropbox", + "Google Drive App Configuration" : "Configuração da aplicação Google Drive", "Personal" : "Pessoal", "System" : "Sistema", "Grant access" : "Conceder acesso", - "Error configuring OAuth1" : "Erro de configuração OAuth1", - "Error configuring OAuth2" : "Erro de configuração OAuth2", + "Error configuring OAuth1" : "Erro ao configurar OAuth1", + "Error configuring OAuth2" : "Erro ao configurar OAuth2", "Generate keys" : "Gerar chaves", "Error generating key pair" : "Erro ao gerar chave par", "All users. Type to select user or group." : "Todos os utilizadores. Digite para selecionar o utilizador ou grupo.", @@ -25,14 +25,14 @@ "Couldn't get the list of external mount points: {type}" : "Não foi possível conseguir a lista de pontos de montagem externos: {type}", "There was an error with message: " : "Houve um erro com a mensagem:", "External mount error" : "Erro de montagem externa", - "external-storage" : "Armazenamento Externo", + "external-storage" : "armazenamento externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível conseguir a lista de pontos de montagem Windows na rede: resposta vazia do servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor, clique na fila vermelha para mais informação", "Please enter the credentials for the {mount} mount" : "Por favor, introduza as credenciais para {mount}", "Username" : "Nome de utilizador", "Password" : "Palavra-passe", "Credentials saved" : "Credenciais guardadas", - "Credentials saving failed" : "Falha ao salvar credenciais", + "Credentials saving failed" : "Falha ao guardar as credenciais", "Credentials required" : "Credenciais necessárias", "Save" : "Guardar", "Storage with id \"%i\" not found" : "Não foi encontrado o armazenamento com a id. \"%i\"", @@ -47,7 +47,7 @@ "Insufficient data: %s" : "Dados insuficientes: %s", "%s" : "%s", "Storage with id \"%i\" is not user editable" : "Armazenamento com id \"%i\" não é editável pelo utilizador", - "Access key" : "Código de acesso", + "Access key" : "Chave de acesso", "Secret key" : "Código secreto", "Builtin" : "Integrado", "None" : "Nenhum", diff --git a/apps/files_external/lib/Controller/GlobalStoragesController.php b/apps/files_external/lib/Controller/GlobalStoragesController.php index 6f9278ce6f1..471e3b51593 100644 --- a/apps/files_external/lib/Controller/GlobalStoragesController.php +++ b/apps/files_external/lib/Controller/GlobalStoragesController.php @@ -139,7 +139,8 @@ class GlobalStoragesController extends StoragesController { $mountOptions, $applicableUsers, $applicableGroups, - $priority + $priority, + $testOnly = true ) { $storage = $this->createStorage( $mountPoint, @@ -172,7 +173,7 @@ class GlobalStoragesController extends StoragesController { ); } - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); return new DataResponse( $storage, diff --git a/apps/files_external/lib/Controller/StoragesController.php b/apps/files_external/lib/Controller/StoragesController.php index aa6a04ecb8d..e50426f4888 100644 --- a/apps/files_external/lib/Controller/StoragesController.php +++ b/apps/files_external/lib/Controller/StoragesController.php @@ -238,7 +238,7 @@ abstract class StoragesController extends Controller { * * @param StorageConfig $storage storage configuration */ - protected function updateStorageStatus(StorageConfig &$storage) { + protected function updateStorageStatus(StorageConfig &$storage, $testOnly = true) { try { $this->manipulateStorageConfig($storage); @@ -249,7 +249,8 @@ abstract class StoragesController extends Controller { \OC_Mount_Config::getBackendStatus( $backend->getStorageClass(), $storage->getBackendOptions(), - false + false, + $testOnly ) ); } catch (InsufficientDataForMeaningfulAnswerException $e) { @@ -293,11 +294,11 @@ abstract class StoragesController extends Controller { * * @return DataResponse */ - public function show($id) { + public function show($id, $testOnly = true) { try { $storage = $this->service->getStorage($id); - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); } catch (NotFoundException $e) { return new DataResponse( [ diff --git a/apps/files_external/lib/Controller/UserGlobalStoragesController.php b/apps/files_external/lib/Controller/UserGlobalStoragesController.php index 1c94a1e9635..f65e578507d 100644 --- a/apps/files_external/lib/Controller/UserGlobalStoragesController.php +++ b/apps/files_external/lib/Controller/UserGlobalStoragesController.php @@ -111,11 +111,11 @@ class UserGlobalStoragesController extends StoragesController { * * @NoAdminRequired */ - public function show($id) { + public function show($id, $testOnly = true) { try { $storage = $this->service->getStorage($id); - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); } catch (NotFoundException $e) { return new DataResponse( [ @@ -146,7 +146,8 @@ class UserGlobalStoragesController extends StoragesController { */ public function update( $id, - $backendOptions + $backendOptions, + $testOnly = true ) { try { $storage = $this->service->getStorage($id); @@ -171,7 +172,7 @@ class UserGlobalStoragesController extends StoragesController { ); } - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); $this->sanitizeStorage($storage); return new DataResponse( diff --git a/apps/files_external/lib/Controller/UserStoragesController.php b/apps/files_external/lib/Controller/UserStoragesController.php index 936da7ec5e2..28663090e89 100644 --- a/apps/files_external/lib/Controller/UserStoragesController.php +++ b/apps/files_external/lib/Controller/UserStoragesController.php @@ -101,8 +101,8 @@ class UserStoragesController extends StoragesController { * * {@inheritdoc} */ - public function show($id) { - return parent::show($id); + public function show($id, $testOnly = true) { + return parent::show($id, $testOnly); } /** @@ -170,7 +170,8 @@ class UserStoragesController extends StoragesController { $backend, $authMechanism, $backendOptions, - $mountOptions + $mountOptions, + $testOnly = true ) { $storage = $this->createStorage( $mountPoint, @@ -200,7 +201,7 @@ class UserStoragesController extends StoragesController { ); } - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); return new DataResponse( $storage, diff --git a/apps/files_external/lib/Lib/Storage/Google.php b/apps/files_external/lib/Lib/Storage/Google.php index 2a1ff768e2c..96f12800c10 100644 --- a/apps/files_external/lib/Lib/Storage/Google.php +++ b/apps/files_external/lib/Lib/Storage/Google.php @@ -97,6 +97,9 @@ class Google extends \OC\Files\Storage\Common { private function getDriveFile($path) { // Remove leading and trailing slashes $path = trim($path, '/'); + if ($path === '.') { + $path = ''; + } if (isset($this->driveFiles[$path])) { return $this->driveFiles[$path]; } else if ($path === '') { @@ -138,7 +141,7 @@ class Google extends \OC\Files\Storage\Common { if ($pos !== false) { $pathWithoutExt = substr($path, 0, $pos); $file = $this->getDriveFile($pathWithoutExt); - if ($file) { + if ($file && $this->isGoogleDocFile($file)) { // Switch cached Google_Service_Drive_DriveFile to the correct index unset($this->driveFiles[$pathWithoutExt]); $this->driveFiles[$path] = $file; @@ -208,6 +211,17 @@ class Google extends \OC\Files\Storage\Common { } } + /** + * Returns whether the given drive file is a Google Doc file + * + * @param \Google_Service_Drive_DriveFile + * + * @return true if the file is a Google Doc file, false otherwise + */ + private function isGoogleDocFile($file) { + return $this->getGoogleDocExtension($file->getMimeType()) !== ''; + } + public function mkdir($path) { if (!$this->is_dir($path)) { $parentFolder = $this->getDriveFile(dirname($path)); @@ -312,7 +326,7 @@ class Google extends \OC\Files\Storage\Common { $stat['size'] = 0; } else { // Check if this is a Google Doc - if ($this->getMimeType($path) !== $file->getMimeType()) { + if ($this->isGoogleDocFile($file)) { // Return unknown file size $stat['size'] = \OCP\Files\FileInfo::SPACE_UNKNOWN; } else { diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 86aafcf5770..3510b675d4d 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -215,7 +215,7 @@ class OC_Mount_Config { * @return int see self::STATUS_* * @throws Exception */ - public static function getBackendStatus($class, $options, $isPersonal) { + public static function getBackendStatus($class, $options, $isPersonal, $testOnly = true) { if (self::$skipTest) { return StorageNotAvailableException::STATUS_SUCCESS; } @@ -228,7 +228,7 @@ class OC_Mount_Config { $storage = new $class($options); try { - $result = $storage->test($isPersonal); + $result = $storage->test($isPersonal, $testOnly); $storage->setAvailability($result); if ($result) { return StorageNotAvailableException::STATUS_SUCCESS; diff --git a/apps/files_external/tests/Storage/GoogleTest.php b/apps/files_external/tests/Storage/GoogleTest.php index 7684fec8429..eb19cc463b1 100644 --- a/apps/files_external/tests/Storage/GoogleTest.php +++ b/apps/files_external/tests/Storage/GoogleTest.php @@ -60,4 +60,13 @@ class GoogleTest extends \Test\Files\Storage\Storage { parent::tearDown(); } + + public function testSameNameAsFolderWithExtension() { + $this->assertTrue($this->instance->mkdir('testsamename')); + $this->assertEquals(13, $this->instance->file_put_contents('testsamename.txt', 'some contents')); + $this->assertEquals('some contents', $this->instance->file_get_contents('testsamename.txt')); + $this->assertTrue($this->instance->is_dir('testsamename')); + $this->assertTrue($this->instance->unlink('testsamename.txt')); + $this->assertTrue($this->instance->rmdir('testsamename')); + } } diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js index 7aa49b2c82a..8f01c16b38c 100644 --- a/apps/files_external/tests/js/settingsSpec.js +++ b/apps/files_external/tests/js/settingsSpec.js @@ -223,7 +223,8 @@ describe('OCA.External.Settings tests', function() { applicableGroups: [], mountOptions: { 'previews': true - } + }, + testOnly: true }); // TODO: respond and check data-id diff --git a/apps/files_sharing/l10n/pl.js b/apps/files_sharing/l10n/pl.js index a703ef087ee..ebdfe2aa738 100644 --- a/apps/files_sharing/l10n/pl.js +++ b/apps/files_sharing/l10n/pl.js @@ -21,6 +21,7 @@ OC.L10N.register( "Remote share password" : "Hasło do zdalnego zasobu", "Cancel" : "Anuluj", "Add remote share" : "Dodaj zdalny zasób", + "No ownCloud installation (7 or higher) found at {remote}" : "Nie znaleziono instalacji ownCloud (w wersji 7 lub nowszej) na {remote}", "Invalid ownCloud url" : "Błędny adres URL", "Shared by" : "Udostępniane przez", "Sharing" : "Udostępnianie", diff --git a/apps/files_sharing/l10n/pl.json b/apps/files_sharing/l10n/pl.json index 62d6ef25b38..f5de5dd368c 100644 --- a/apps/files_sharing/l10n/pl.json +++ b/apps/files_sharing/l10n/pl.json @@ -19,6 +19,7 @@ "Remote share password" : "Hasło do zdalnego zasobu", "Cancel" : "Anuluj", "Add remote share" : "Dodaj zdalny zasób", + "No ownCloud installation (7 or higher) found at {remote}" : "Nie znaleziono instalacji ownCloud (w wersji 7 lub nowszej) na {remote}", "Invalid ownCloud url" : "Błędny adres URL", "Shared by" : "Udostępniane przez", "Sharing" : "Udostępnianie", diff --git a/apps/files_sharing/lib/Cache.php b/apps/files_sharing/lib/Cache.php index 82d885a8ef3..a0519cadbaa 100644 --- a/apps/files_sharing/lib/Cache.php +++ b/apps/files_sharing/lib/Cache.php @@ -81,7 +81,7 @@ class Cache extends CacheJail { } protected function formatCacheEntry($entry) { - $path = $entry['path']; + $path = isset($entry['path']) ? $entry['path'] : ''; $entry = parent::formatCacheEntry($entry); $sharePermissions = $this->storage->getPermissions($path); if (isset($entry['permissions'])) { diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 965c4d36cad..b5b5e416884 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -415,4 +415,22 @@ class Shared extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage { return $this->sourceStorage; } + public function file_get_contents($path) { + $info = [ + 'target' => $this->getMountPoint() . '/' . $path, + 'source' => $this->getSourcePath($path), + ]; + \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); + return parent::file_get_contents($path); + } + + public function file_put_contents($path, $data) { + $info = [ + 'target' => $this->getMountPoint() . '/' . $path, + 'source' => $this->getSourcePath($path), + ]; + \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); + return parent::file_put_contents($path, $data); + } + } diff --git a/apps/systemtags/l10n/fr.js b/apps/systemtags/l10n/fr.js index 93c7d364b24..387bc45643c 100644 --- a/apps/systemtags/l10n/fr.js +++ b/apps/systemtags/l10n/fr.js @@ -7,20 +7,20 @@ OC.L10N.register( "Please select tags to filter by" : "Veuillez sélectionner les étiquettes par lesquelles filtrer", "No files found for the selected tags" : "Aucun fichier pour les étiquettes sélectionnées", "<strong>System tags</strong> for a file have been modified" : "<strong>Les étiquettes systèmes</strong> pour un fichier ont été modifiées", - "You assigned system tag %3$s" : "Vous avez attribué l'étiquette système %3$s", + "You assigned system tag %3$s" : "Vous avez attribué l'étiquette collaborative %3$s", "%1$s assigned system tag %3$s" : "%1$s a attribué l'étiquette système %3$s", - "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette système %3$s", + "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette collaborative %3$s", "%1$s unassigned system tag %3$s" : "%1$s a retiré l'étiquette système %3$s", - "You created system tag %2$s" : "Vous avez créé l'étiquette système %2$s", + "You created system tag %2$s" : "Vous avez créé l'étiquette collaborative %2$s", "%1$s created system tag %2$s" : "%1$s a créé l'étiquette système %2$s", - "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette système %2$s", - "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette système %2$s", - "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette système %3$s en %2$s", - "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette système %3$s en %2$s", - "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette système %3$s à %2$s", - "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette système %3$s à %2$s", - "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette système %3$s de %2$s", - "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette système %3$s à %2$s", + "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette collaborative %2$s", + "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette collaborative %2$s", + "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette collaborative %3$s en %2$s", + "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette collaborative %3$s en %2$s", + "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette collaborative %3$s à %2$s", + "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette collaborative %3$s à %2$s", + "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette collaborative %3$s à %2$s", + "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette collaborative %3$s à %2$s", "%s (restricted)" : "%s (restreint)", "%s (invisible)" : "%s (invisible)", "No files in here" : "Aucun fichier", diff --git a/apps/systemtags/l10n/fr.json b/apps/systemtags/l10n/fr.json index 94254c93ab0..66b4d246dac 100644 --- a/apps/systemtags/l10n/fr.json +++ b/apps/systemtags/l10n/fr.json @@ -5,20 +5,20 @@ "Please select tags to filter by" : "Veuillez sélectionner les étiquettes par lesquelles filtrer", "No files found for the selected tags" : "Aucun fichier pour les étiquettes sélectionnées", "<strong>System tags</strong> for a file have been modified" : "<strong>Les étiquettes systèmes</strong> pour un fichier ont été modifiées", - "You assigned system tag %3$s" : "Vous avez attribué l'étiquette système %3$s", + "You assigned system tag %3$s" : "Vous avez attribué l'étiquette collaborative %3$s", "%1$s assigned system tag %3$s" : "%1$s a attribué l'étiquette système %3$s", - "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette système %3$s", + "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette collaborative %3$s", "%1$s unassigned system tag %3$s" : "%1$s a retiré l'étiquette système %3$s", - "You created system tag %2$s" : "Vous avez créé l'étiquette système %2$s", + "You created system tag %2$s" : "Vous avez créé l'étiquette collaborative %2$s", "%1$s created system tag %2$s" : "%1$s a créé l'étiquette système %2$s", - "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette système %2$s", - "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette système %2$s", - "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette système %3$s en %2$s", - "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette système %3$s en %2$s", - "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette système %3$s à %2$s", - "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette système %3$s à %2$s", - "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette système %3$s de %2$s", - "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette système %3$s à %2$s", + "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette collaborative %2$s", + "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette collaborative %2$s", + "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette collaborative %3$s en %2$s", + "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette collaborative %3$s en %2$s", + "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette collaborative %3$s à %2$s", + "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette collaborative %3$s à %2$s", + "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette collaborative %3$s à %2$s", + "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette collaborative %3$s à %2$s", "%s (restricted)" : "%s (restreint)", "%s (invisible)" : "%s (invisible)", "No files in here" : "Aucun fichier", diff --git a/apps/systemtags/l10n/pl.js b/apps/systemtags/l10n/pl.js index fc3616bff26..f5e4775d540 100644 --- a/apps/systemtags/l10n/pl.js +++ b/apps/systemtags/l10n/pl.js @@ -3,6 +3,8 @@ OC.L10N.register( { "Tags" : "Etykiety", "Tagged files" : "Otagowane pliki", + "Select tags to filter by" : "Wybierz tagi do filtru", + "Please select tags to filter by" : "Proszę wybrać tagi do filtrów", "No files found for the selected tags" : "Nie znaleziono plików dla wybranych etykiet", "<strong>System tags</strong> for a file have been modified" : "<strong>System etykiet</strong> dla pliku został zmieniony", "%1$s assigned system tag %3$s" : "%1$s przypisywalny system etykiet%3$s", diff --git a/apps/systemtags/l10n/pl.json b/apps/systemtags/l10n/pl.json index 543aa3be58c..6cb103ed4a5 100644 --- a/apps/systemtags/l10n/pl.json +++ b/apps/systemtags/l10n/pl.json @@ -1,6 +1,8 @@ { "translations": { "Tags" : "Etykiety", "Tagged files" : "Otagowane pliki", + "Select tags to filter by" : "Wybierz tagi do filtru", + "Please select tags to filter by" : "Proszę wybrać tagi do filtrów", "No files found for the selected tags" : "Nie znaleziono plików dla wybranych etykiet", "<strong>System tags</strong> for a file have been modified" : "<strong>System etykiet</strong> dla pliku został zmieniony", "%1$s assigned system tag %3$s" : "%1$s przypisywalny system etykiet%3$s", diff --git a/apps/updatenotification/l10n/pl.js b/apps/updatenotification/l10n/pl.js index 6551d0808e3..d86fdf3c243 100644 --- a/apps/updatenotification/l10n/pl.js +++ b/apps/updatenotification/l10n/pl.js @@ -1,8 +1,11 @@ OC.L10N.register( "updatenotification", { + "Update notifications" : "Powiadomienia o aktualizacji", "{version} is available. Get more information on how to update." : "Wersja {version} jest dostępna. Dowiedz się jak zaktualizować.", "Updated channel" : "Zaktualizowano kanał", + "ownCloud core" : "Rdzeń ownCloud", + "Update for %1$s to version %2$s is available." : "Jest dostępna aktualizacja dla %1$s do wersji %2$s", "Updater" : "Aktualizator", "A new version is available: %s" : "Dostępna jest nowa wersja: %s", "Open updater" : "Otwórz aktualizator", diff --git a/apps/updatenotification/l10n/pl.json b/apps/updatenotification/l10n/pl.json index fd859feae11..b5d7132d9f0 100644 --- a/apps/updatenotification/l10n/pl.json +++ b/apps/updatenotification/l10n/pl.json @@ -1,6 +1,9 @@ { "translations": { + "Update notifications" : "Powiadomienia o aktualizacji", "{version} is available. Get more information on how to update." : "Wersja {version} jest dostępna. Dowiedz się jak zaktualizować.", "Updated channel" : "Zaktualizowano kanał", + "ownCloud core" : "Rdzeń ownCloud", + "Update for %1$s to version %2$s is available." : "Jest dostępna aktualizacja dla %1$s do wersji %2$s", "Updater" : "Aktualizator", "A new version is available: %s" : "Dostępna jest nowa wersja: %s", "Open updater" : "Otwórz aktualizator", diff --git a/autotest.sh b/autotest.sh index bd51cddefcf..e798157fe64 100755 --- a/autotest.sh +++ b/autotest.sh @@ -54,7 +54,7 @@ else fi if ! [ -x "$PHPUNIT" ]; then - echo "phpunit executable not found, please install phpunit version >= 3.7" >&2 + echo "phpunit executable not found, please install phpunit version >= 4.4" >&2 exit 3 fi diff --git a/build/integration/features/bootstrap/BasicStructure.php b/build/integration/features/bootstrap/BasicStructure.php index b8fb516fada..9248b2cd252 100644 --- a/build/integration/features/bootstrap/BasicStructure.php +++ b/build/integration/features/bootstrap/BasicStructure.php @@ -261,6 +261,17 @@ trait BasicStructure { } /** + * @Given User :user modifies text of :filename with text :text + * @param string $user + * @param string $filename + * @param string $text + */ + public function modifyTextOfFile($user, $filename, $text) { + self::removeFile("../../data/$user/files", "$filename"); + file_put_contents("../../data/$user/files" . "$filename", "$text"); + } + + /** * @BeforeSuite */ public static function addFilesToSkeleton(){ diff --git a/build/integration/features/bootstrap/FederationContext.php b/build/integration/features/bootstrap/FederationContext.php index 2809c6974fa..55f3a55da0d 100644 --- a/build/integration/features/bootstrap/FederationContext.php +++ b/build/integration/features/bootstrap/FederationContext.php @@ -12,7 +12,7 @@ require __DIR__ . '/../../vendor/autoload.php'; */ class FederationContext implements Context, SnippetAcceptingContext { - use Sharing; + use WebDav; /** * @Given /^User "([^"]*)" from server "(LOCAL|REMOTE)" shares "([^"]*)" with user "([^"]*)" from server "(LOCAL|REMOTE)"$/ diff --git a/build/integration/federation_features/federated.feature b/build/integration/federation_features/federated.feature index acd1f91e908..8bf8e921b0f 100644 --- a/build/integration/federation_features/federated.feature +++ b/build/integration/federation_features/federated.feature @@ -120,6 +120,67 @@ Feature: federated | share_with | user2 | | share_with_displayname | user2 | + Scenario: Overwrite a federated shared file as recipient + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA" + When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/textfile0 (2).txt" + And Downloading file "/textfile0 (2).txt" with range "bytes=0-8" + Then Downloaded content should be "BLABLABLA" + + Scenario: Overwrite a federated shared folder as recipient + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA" + When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/PARENT (2)/textfile0.txt" + And Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=0-8" + Then Downloaded content should be "BLABLABLA" + + Scenario: Overwrite a federated shared file as recipient using old chunking + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/textfile0 (2).txt" + And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/textfile0 (2).txt" + And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/textfile0 (2).txt" + When Downloading file "/textfile0 (2).txt" with range "bytes=0-4" + Then Downloaded content should be "AAAAA" + + Scenario: Overwrite a federated shared folder as recipient using old chunking + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/PARENT (2)/textfile0.txt" + And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/PARENT (2)/textfile0.txt" + And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/PARENT (2)/textfile0.txt" + When Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=3-13" + Then Downloaded content should be "AABBBBBCCCC" diff --git a/core/Application.php b/core/Application.php index 25e2fa76273..a87917b626a 100644 --- a/core/Application.php +++ b/core/Application.php @@ -120,7 +120,8 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('UserManager'), - $c->query('OC\Authentication\Token\DefaultTokenProvider'), + $c->query('ServerContainer')->query('OC\Authentication\Token\IProvider'), + $c->query('TwoFactorAuthManager'), $c->query('SecureRandom') ); }); diff --git a/core/Command/Encryption/DecryptAll.php b/core/Command/Encryption/DecryptAll.php index 8d7d26f3d23..83c6c1dc168 100644 --- a/core/Command/Encryption/DecryptAll.php +++ b/core/Command/Encryption/DecryptAll.php @@ -111,7 +111,8 @@ class DecryptAll extends Command { $this->addArgument( 'user', InputArgument::OPTIONAL, - 'user for which you want to decrypt all files (optional)' + 'user for which you want to decrypt all files (optional)', + '' ); } @@ -127,8 +128,15 @@ class DecryptAll extends Command { return; } + $uid = $input->getArgument('user'); + if ($uid === '') { + $message = 'your ownCloud'; + } else { + $message = "$uid's account"; + } + $output->writeln("\n"); - $output->writeln('You are about to start to decrypt all files stored in your ownCloud.'); + $output->writeln("You are about to start to decrypt all files stored in $message."); $output->writeln('It will depend on the encryption module and your setup if this is possible.'); $output->writeln('Depending on the number and size of your files this can take some time'); $output->writeln('Please make sure that no user access his files during this process!'); @@ -140,6 +148,7 @@ class DecryptAll extends Command { $result = $this->decryptAll->decryptAll($input, $output, $user); if ($result === false) { $output->writeln(' aborted.'); + $output->writeln('Server side encryption remains enabled'); $this->config->setAppValue('core', 'encryption_enabled', 'yes'); } $this->resetSingleUserAndTrashbin(); diff --git a/core/Controller/TokenController.php b/core/Controller/TokenController.php index 42cc29bad10..13b1db9044a 100644 --- a/core/Controller/TokenController.php +++ b/core/Controller/TokenController.php @@ -1,4 +1,5 @@ <?php + /** * @author Christoph Wurst <christoph@owncloud.com> * @@ -23,22 +24,27 @@ namespace OC\Core\Controller; use OC\AppFramework\Http; use OC\Authentication\Token\DefaultTokenProvider; +use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; -use OC\User\Manager; +use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager; +use OC\User\Manager as UserManager; +use OCA\User_LDAP\User\Manager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\Http\Response; use OCP\IRequest; use OCP\Security\ISecureRandom; class TokenController extends Controller { - /** @var Manager */ + /** @var UserManager */ private $userManager; - /** @var DefaultTokenProvider */ + /** @var IProvider */ private $tokenProvider; + /** @var TwoFactorAuthManager */ + private $twoFactorAuthManager; + /** @var ISecureRandom */ private $secureRandom; @@ -49,12 +55,12 @@ class TokenController extends Controller { * @param DefaultTokenProvider $tokenProvider * @param ISecureRandom $secureRandom */ - public function __construct($appName, IRequest $request, Manager $userManager, DefaultTokenProvider $tokenProvider, - ISecureRandom $secureRandom) { + public function __construct($appName, IRequest $request, UserManager $userManager, IProvider $tokenProvider, TwoFactorAuthManager $twoFactorAuthManager, ISecureRandom $secureRandom) { parent::__construct($appName, $request); $this->userManager = $userManager; $this->tokenProvider = $tokenProvider; $this->secureRandom = $secureRandom; + $this->twoFactorAuthManager = $twoFactorAuthManager; } /** @@ -70,18 +76,26 @@ class TokenController extends Controller { */ public function generateToken($user, $password, $name = 'unknown client') { if (is_null($user) || is_null($password)) { - $response = new Response(); + $response = new JSONResponse(); $response->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY); return $response; } - $loginResult = $this->userManager->checkPassword($user, $password); - if ($loginResult === false) { - $response = new Response(); + $loginName = $user; + $user = $this->userManager->checkPassword($loginName, $password); + if ($user === false) { + $response = new JSONResponse(); $response->setStatus(Http::STATUS_UNAUTHORIZED); return $response; } + + if ($this->twoFactorAuthManager->isTwoFactorAuthenticated($user)) { + $resp = new JSONResponse(); + $resp->setStatus(Http::STATUS_UNAUTHORIZED); + return $resp; + } + $token = $this->secureRandom->generate(128); - $this->tokenProvider->generateToken($token, $loginResult->getUID(), $user, $password, $name, IToken::PERMANENT_TOKEN); + $this->tokenProvider->generateToken($token, $user->getUID(), $loginName, $password, $name, IToken::PERMANENT_TOKEN); return [ 'token' => $token, ]; diff --git a/core/Controller/TwoFactorChallengeController.php b/core/Controller/TwoFactorChallengeController.php index 499898de3bc..edaf3378cd8 100644 --- a/core/Controller/TwoFactorChallengeController.php +++ b/core/Controller/TwoFactorChallengeController.php @@ -62,6 +62,13 @@ class TwoFactorChallengeController extends Controller { } /** + * @return string + */ + protected function getLogoutAttribute() { + return \OC_User::getLogoutAttribute(); + } + + /** * @NoAdminRequired * @NoCSRFRequired * @@ -75,6 +82,7 @@ class TwoFactorChallengeController extends Controller { $data = [ 'providers' => $providers, 'redirect_url' => $redirect_url, + 'logout_attribute' => $this->getLogoutAttribute(), ]; return new TemplateResponse($this->appName, 'twofactorselectchallenge', $data, 'guest'); } @@ -106,6 +114,7 @@ class TwoFactorChallengeController extends Controller { $data = [ 'error' => $error, 'provider' => $provider, + 'logout_attribute' => $this->getLogoutAttribute(), 'template' => $tmpl->fetchPage(), ]; return new TemplateResponse($this->appName, 'twofactorshowchallenge', $data, 'guest'); diff --git a/core/Middleware/TwoFactorMiddleware.php b/core/Middleware/TwoFactorMiddleware.php index aa82897ad46..0bad8a2c40f 100644 --- a/core/Middleware/TwoFactorMiddleware.php +++ b/core/Middleware/TwoFactorMiddleware.php @@ -82,6 +82,11 @@ class TwoFactorMiddleware extends Middleware { return; } + if ($controller instanceof \OC\Core\Controller\LoginController && $methodName === 'logout') { + // Don't block the logout page, to allow canceling the 2FA + return; + } + if ($this->userSession->isLoggedIn()) { $user = $this->userSession->getUser(); diff --git a/core/css/styles.css b/core/css/styles.css index df9509baa19..475c4fa3fb3 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -38,6 +38,10 @@ body { display: inline-block; } +a.two-factor-cancel { + color: #fff; +} + .float-spinner { height: 32px; display: none; diff --git a/core/css/tooltip.css b/core/css/tooltip.css index 34d0ec6c70f..af25fd5533d 100644 --- a/core/css/tooltip.css +++ b/core/css/tooltip.css @@ -47,7 +47,7 @@ padding: 0 5px; } .tooltip-inner { - max-width: 200px; + max-width: 350px; padding: 3px 8px; color: #ffffff; text-align: center; diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js index 4cc50e51ae6..f987c9f04e6 100644 --- a/core/js/setupchecks.js +++ b/core/js/setupchecks.js @@ -197,7 +197,7 @@ } var afterCall = function(xhr) { var messages = []; - if (xhr.status !== 403 && xhr.status !== 307 && xhr.status !== 301 && xhr.responseText === '') { + if (xhr.status !== 403 && xhr.status !== 307 && xhr.status !== 301 && xhr.responseText !== '') { messages.push({ msg: t('core', 'Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root.'), type: OC.SetupChecks.MESSAGE_TYPE_ERROR @@ -208,7 +208,7 @@ $.ajax({ type: 'GET', - url: OC.linkTo('', oc_dataURL+'/.ocdata'), + url: OC.linkTo('', oc_dataURL+'/htaccesstest.txt?t=' + (new Date()).getTime()), complete: afterCall }); return deferred.promise(); diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js index 4931ca990da..172e6e27135 100644 --- a/core/js/tests/specs/setupchecksSpec.js +++ b/core/js/tests/specs/setupchecksSpec.js @@ -103,7 +103,7 @@ describe('OC.SetupChecks tests', function() { it('should return an error if data directory is not protected', function(done) { var async = OC.SetupChecks.checkDataProtected(); - suite.server.requests[0].respond(200); + suite.server.requests[0].respond(200, {'Content-Type': 'text/plain'}, 'file contents'); async.done(function( data, s, x ){ expect(data).toEqual([ diff --git a/core/l10n/ja.js b/core/l10n/ja.js index 397a417eb9d..a45c8620c6e 100644 --- a/core/l10n/ja.js +++ b/core/l10n/ja.js @@ -295,6 +295,7 @@ OC.L10N.register( "This means only administrators can use the instance." : "これは、管理者のみがインスタンスを利用できることを意味しています。", "Contact your system administrator if this message persists or appeared unexpectedly." : "このメッセージが引き続きもしくは予期せず現れる場合は、システム管理者に問い合わせてください。", "Thank you for your patience." : "しばらくお待ちください。", + "Two-step verification" : "2段階認証", "You are accessing the server from an untrusted domain." : "信頼されていないドメインからサーバーにアクセスしています。", "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "管理者に問い合わせてください。このサーバーの管理者の場合は、\"trusted_domain\" の設定を config/config.php に設定してください。config/config.sample.php にサンプルの設定方法が記載してあります。", "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "環境により、下のボタンで信頼するドメインに追加する必要があるかもしれません。", diff --git a/core/l10n/ja.json b/core/l10n/ja.json index 11c88968ee2..b5e9ba9b9d0 100644 --- a/core/l10n/ja.json +++ b/core/l10n/ja.json @@ -293,6 +293,7 @@ "This means only administrators can use the instance." : "これは、管理者のみがインスタンスを利用できることを意味しています。", "Contact your system administrator if this message persists or appeared unexpectedly." : "このメッセージが引き続きもしくは予期せず現れる場合は、システム管理者に問い合わせてください。", "Thank you for your patience." : "しばらくお待ちください。", + "Two-step verification" : "2段階認証", "You are accessing the server from an untrusted domain." : "信頼されていないドメインからサーバーにアクセスしています。", "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "管理者に問い合わせてください。このサーバーの管理者の場合は、\"trusted_domain\" の設定を config/config.php に設定してください。config/config.sample.php にサンプルの設定方法が記載してあります。", "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "環境により、下のボタンで信頼するドメインに追加する必要があるかもしれません。", diff --git a/core/templates/twofactorselectchallenge.php b/core/templates/twofactorselectchallenge.php index 14d599aab3e..4209beac4e6 100644 --- a/core/templates/twofactorselectchallenge.php +++ b/core/templates/twofactorselectchallenge.php @@ -18,4 +18,5 @@ </li> <?php endforeach; ?> </ul> -</fieldset>
\ No newline at end of file +</fieldset> +<a class="two-factor-cancel" <?php print_unescaped($_['logout_attribute']); ?>><?php p($l->t('Cancel login')) ?></a> diff --git a/core/templates/twofactorshowchallenge.php b/core/templates/twofactorshowchallenge.php index 66f5ed312ec..c5ee9aca4b4 100644 --- a/core/templates/twofactorshowchallenge.php +++ b/core/templates/twofactorshowchallenge.php @@ -17,3 +17,4 @@ $template = $_['template']; <span class="warning"><?php p($l->t('An error occured while verifying the token')); ?></span> <?php endif; ?> <?php print_unescaped($template); ?> +<a class="two-factor-cancel" <?php print_unescaped($_['logout_attribute']); ?>><?php p($l->t('Cancel login')) ?></a> diff --git a/lib/l10n/ar.js b/lib/l10n/ar.js index 21a67883ac3..a7a3e4e4c31 100644 --- a/lib/l10n/ar.js +++ b/lib/l10n/ar.js @@ -1,18 +1,46 @@ OC.L10N.register( "lib", { + "Cannot write into \"config\" directory!" : "الكتابة في مجلد \"config\" غير ممكنة!", + "This can usually be fixed by giving the webserver write access to the config directory" : "يمكن حل هذا عادة بإعطاء خادم الوب صلاحية الكتابة في مجلد config", + "See %s" : "أنظر %s", + "Sample configuration detected" : "تم اكتشاف إعدادات عيّنة", + "PHP %s or higher is required." : "إصدار PHP %s أو أحدث منه مطلوب.", + "PHP with a version lower than %s is required." : "PHP الإصدار %s أو أقل مطلوب.", + "%sbit or higher PHP required." : "مكتبات PHP ذات %s بت أو أعلى مطلوبة.", + "Following databases are supported: %s" : "قواعد البيانات التالية مدعومة: %s", + "The command line tool %s could not be found" : "لم يتم العثور على أداة سطر الأوامر %s", + "The library %s is not available." : "مكتبة %s غير متوفرة.", "Unknown filetype" : "نوع الملف غير معروف", "Invalid image" : "الصورة غير صالحة", "today" : "اليوم", "yesterday" : "يوم أمس", + "_%n day ago_::_%n days ago_" : ["قبل ساعات","قبل يوم","قبل يومين","قبل %n يوماً","قبل %n يوماً","قبل %n يوماً"], "last month" : "الشهر الماضي", + "_%n month ago_::_%n months ago_" : ["قبل عدة أيام","قبل شهر","قبل شهرين","قبل %n شهراً","قبل %n شهراً","قبل %n شهراً"], "last year" : "السنةالماضية", "seconds ago" : "منذ ثواني", + "Empty filename is not allowed" : "لا يسمح بأسماء فارغة للملفات", + "4-byte characters are not supported in file names" : "المحارف ذات 4 بايت غير مسموح بها في أسماء الملفات", + "File name is a reserved word" : "اسم الملف كلمة محجوزة", + "File name contains at least one invalid character" : "اسم الملف به ، على الأقل ، حرف غير صالح", + "File name is too long" : "اسم الملف طويل جداً", "App directory already exists" : "مجلد التطبيق موجود مسبقا", "Can't create app folder. Please fix permissions. %s" : "لا يمكن إنشاء مجلد التطبيق. يرجى تعديل الصلاحيات. %s", + "Archive does not contain a directory named %s" : "الأرشيف لا يحتوي مجلداً اسمه %s", "No source specified when installing app" : "لم يتم تحديد المصدر عن تثبيت البرنامج", + "No href specified when installing app from http" : "لم يتم تحديد href عند تثبيت التطبيق من http", + "No path specified when installing app from local file" : "لم يتم تحديد مسار عند تثبيت التطبيق من ملف محلّي", "Archives of type %s are not supported" : "الأرشيفات من نوع %s غير مدعومة", + "Failed to open archive when installing app" : "فشل فتح الأرشيف أثناء تثبيت التطبيق", "App does not provide an info.xml file" : "التطبيق لا يتوفر على ملف info.xml", + "App cannot be installed because appinfo file cannot be read." : "لا يمكن تثبيت التطبيق لأن ملف appinfo غير ممكنة قراءته.", + "Signature could not get checked. Please contact the app developer and check your admin screen." : "لم يتم التحقق من التوقيع. فضلاً اتصل بمطوّر التطبيق و تحقق من شاشة الإدارة في حسابك.", + "App can't be installed because of not allowed code in the App" : "لم يتم تثبيت التطبيق لوجود شفرة غير مسموح بها في التطبيق", + "App can't be installed because it is not compatible with this version of ownCloud" : "لم يتم تثبيت التطبيق لأنه غير متوافق مع هذا الإصدار من ownCloud", + "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" : "لم يتم تثبيت التطبيق لأن به علامة <shipped>true</shipped> التي لايسمح بها في التطبيقات غير المشحونة", + "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "لم يتم تثبيت التطبيق لأن الإصدار في info.xml مختلف عن الإصدار المذكور في متجر التطبيقات", + "%s enter the database username and name." : "%s أدخِل اسم قاعدة البيانات واسم مستخدمها.", "%s enter the database username." : "%s ادخل اسم المستخدم الخاص بقاعدة البيانات.", "%s enter the database name." : "%s ادخل اسم فاعدة البيانات", "%s you may not use dots in the database name" : "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات", @@ -23,9 +51,20 @@ OC.L10N.register( "You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", "Offending command was: \"%s\", name: %s, password: %s" : "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s", "PostgreSQL username and/or password not valid" : "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة", + "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "نظام ماك الإصدار X غير مدعوم و %s لن يعمل بشكل صحيح على هذه المنصة. استخدمه على مسؤوليتك!", + "For the best results, please consider using a GNU/Linux server instead." : "فضلاً ضع في الاعتبار استخدام نظام GNU/Linux بدل الأنظمة الأخرى للحصول على أفضل النتائج.", + "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "فضلاً إحذف إعداد open_basedir من ملف php.ini لديك أو حوّل إلى PHP إصدار 64 بت.", "Set an admin username." : "اعداد اسم مستخدم للمدير", "Set an admin password." : "اعداد كلمة مرور للمدير", + "Can't create or write into the data directory %s" : "لا يمكن الإنشاء أو الكتابة في مجلد البيانات %s", + "Invalid Federated Cloud ID" : "معرّف سحابة الاتحاد غير صالح", "%s shared »%s« with you" : "%s شارك »%s« معك", + "%s via %s" : "%s عبر %s", + "Sharing %s failed, because the file does not exist" : "فشلت مشاركة %s فالملف غير موجود", + "You are not allowed to share %s" : "أنت غير مسموح لك أن تشارك %s", + "Sharing %s failed, because you can not share with yourself" : "فشلت مشاركة %s لأنك لايمكنك المشاركة مع نفسك", + "Sharing %s failed, because the user %s does not exist" : "فشلت مشاركة %s لأن المستخدم %s غير موجود", + "Share type %s is not valid for %s" : "مشاركة النوع %s غير صالحة لـ %s", "Could not find category \"%s\"" : "تعذر العثور على المجلد \"%s\"", "Apps" : "التطبيقات", "A valid username must be provided" : "يجب ادخال اسم مستخدم صحيح", diff --git a/lib/l10n/ar.json b/lib/l10n/ar.json index eb91606bb52..86284a03538 100644 --- a/lib/l10n/ar.json +++ b/lib/l10n/ar.json @@ -1,16 +1,44 @@ { "translations": { + "Cannot write into \"config\" directory!" : "الكتابة في مجلد \"config\" غير ممكنة!", + "This can usually be fixed by giving the webserver write access to the config directory" : "يمكن حل هذا عادة بإعطاء خادم الوب صلاحية الكتابة في مجلد config", + "See %s" : "أنظر %s", + "Sample configuration detected" : "تم اكتشاف إعدادات عيّنة", + "PHP %s or higher is required." : "إصدار PHP %s أو أحدث منه مطلوب.", + "PHP with a version lower than %s is required." : "PHP الإصدار %s أو أقل مطلوب.", + "%sbit or higher PHP required." : "مكتبات PHP ذات %s بت أو أعلى مطلوبة.", + "Following databases are supported: %s" : "قواعد البيانات التالية مدعومة: %s", + "The command line tool %s could not be found" : "لم يتم العثور على أداة سطر الأوامر %s", + "The library %s is not available." : "مكتبة %s غير متوفرة.", "Unknown filetype" : "نوع الملف غير معروف", "Invalid image" : "الصورة غير صالحة", "today" : "اليوم", "yesterday" : "يوم أمس", + "_%n day ago_::_%n days ago_" : ["قبل ساعات","قبل يوم","قبل يومين","قبل %n يوماً","قبل %n يوماً","قبل %n يوماً"], "last month" : "الشهر الماضي", + "_%n month ago_::_%n months ago_" : ["قبل عدة أيام","قبل شهر","قبل شهرين","قبل %n شهراً","قبل %n شهراً","قبل %n شهراً"], "last year" : "السنةالماضية", "seconds ago" : "منذ ثواني", + "Empty filename is not allowed" : "لا يسمح بأسماء فارغة للملفات", + "4-byte characters are not supported in file names" : "المحارف ذات 4 بايت غير مسموح بها في أسماء الملفات", + "File name is a reserved word" : "اسم الملف كلمة محجوزة", + "File name contains at least one invalid character" : "اسم الملف به ، على الأقل ، حرف غير صالح", + "File name is too long" : "اسم الملف طويل جداً", "App directory already exists" : "مجلد التطبيق موجود مسبقا", "Can't create app folder. Please fix permissions. %s" : "لا يمكن إنشاء مجلد التطبيق. يرجى تعديل الصلاحيات. %s", + "Archive does not contain a directory named %s" : "الأرشيف لا يحتوي مجلداً اسمه %s", "No source specified when installing app" : "لم يتم تحديد المصدر عن تثبيت البرنامج", + "No href specified when installing app from http" : "لم يتم تحديد href عند تثبيت التطبيق من http", + "No path specified when installing app from local file" : "لم يتم تحديد مسار عند تثبيت التطبيق من ملف محلّي", "Archives of type %s are not supported" : "الأرشيفات من نوع %s غير مدعومة", + "Failed to open archive when installing app" : "فشل فتح الأرشيف أثناء تثبيت التطبيق", "App does not provide an info.xml file" : "التطبيق لا يتوفر على ملف info.xml", + "App cannot be installed because appinfo file cannot be read." : "لا يمكن تثبيت التطبيق لأن ملف appinfo غير ممكنة قراءته.", + "Signature could not get checked. Please contact the app developer and check your admin screen." : "لم يتم التحقق من التوقيع. فضلاً اتصل بمطوّر التطبيق و تحقق من شاشة الإدارة في حسابك.", + "App can't be installed because of not allowed code in the App" : "لم يتم تثبيت التطبيق لوجود شفرة غير مسموح بها في التطبيق", + "App can't be installed because it is not compatible with this version of ownCloud" : "لم يتم تثبيت التطبيق لأنه غير متوافق مع هذا الإصدار من ownCloud", + "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" : "لم يتم تثبيت التطبيق لأن به علامة <shipped>true</shipped> التي لايسمح بها في التطبيقات غير المشحونة", + "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "لم يتم تثبيت التطبيق لأن الإصدار في info.xml مختلف عن الإصدار المذكور في متجر التطبيقات", + "%s enter the database username and name." : "%s أدخِل اسم قاعدة البيانات واسم مستخدمها.", "%s enter the database username." : "%s ادخل اسم المستخدم الخاص بقاعدة البيانات.", "%s enter the database name." : "%s ادخل اسم فاعدة البيانات", "%s you may not use dots in the database name" : "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات", @@ -21,9 +49,20 @@ "You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", "Offending command was: \"%s\", name: %s, password: %s" : "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s", "PostgreSQL username and/or password not valid" : "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة", + "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "نظام ماك الإصدار X غير مدعوم و %s لن يعمل بشكل صحيح على هذه المنصة. استخدمه على مسؤوليتك!", + "For the best results, please consider using a GNU/Linux server instead." : "فضلاً ضع في الاعتبار استخدام نظام GNU/Linux بدل الأنظمة الأخرى للحصول على أفضل النتائج.", + "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "فضلاً إحذف إعداد open_basedir من ملف php.ini لديك أو حوّل إلى PHP إصدار 64 بت.", "Set an admin username." : "اعداد اسم مستخدم للمدير", "Set an admin password." : "اعداد كلمة مرور للمدير", + "Can't create or write into the data directory %s" : "لا يمكن الإنشاء أو الكتابة في مجلد البيانات %s", + "Invalid Federated Cloud ID" : "معرّف سحابة الاتحاد غير صالح", "%s shared »%s« with you" : "%s شارك »%s« معك", + "%s via %s" : "%s عبر %s", + "Sharing %s failed, because the file does not exist" : "فشلت مشاركة %s فالملف غير موجود", + "You are not allowed to share %s" : "أنت غير مسموح لك أن تشارك %s", + "Sharing %s failed, because you can not share with yourself" : "فشلت مشاركة %s لأنك لايمكنك المشاركة مع نفسك", + "Sharing %s failed, because the user %s does not exist" : "فشلت مشاركة %s لأن المستخدم %s غير موجود", + "Share type %s is not valid for %s" : "مشاركة النوع %s غير صالحة لـ %s", "Could not find category \"%s\"" : "تعذر العثور على المجلد \"%s\"", "Apps" : "التطبيقات", "A valid username must be provided" : "يجب ادخال اسم مستخدم صحيح", diff --git a/lib/l10n/ro.js b/lib/l10n/ro.js index 5c5b7844cf7..de13cd796d6 100644 --- a/lib/l10n/ro.js +++ b/lib/l10n/ro.js @@ -29,6 +29,7 @@ OC.L10N.register( "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulul cu id-ul %s nu există. Activează-l în setările tale de aplicație sau contactează-ți administratorul.", "Empty filename is not allowed" : "Nu este permis fișier fără nume", "Dot files are not allowed" : "Fișierele care încep cu caracterul punct nu sunt permise", + "4-byte characters are not supported in file names" : "Caracterele stocate în 4 octeți nu sunt suportate în denumirile fișierelor", "File name is a reserved word" : "Numele fișierului este un cuvânt rezervat", "File name contains at least one invalid character" : "Numele fișierului conține măcar un caracter invalid", "File name is too long" : "Numele fișierului este prea lung", @@ -36,6 +37,8 @@ OC.L10N.register( "Can't create app folder. Please fix permissions. %s" : "Nu se poate crea directorul de aplicație. Repară permisiunile. %s", "Archive does not contain a directory named %s" : "Arhiva nu conține vreun director cu numele %s", "No source specified when installing app" : "Nu a fost specificată vreo sursă la instalarea aplicației", + "No href specified when installing app from http" : "Nu s-a specificat adresa la instalarea aplicației dintr-o sursă de pe Internet", + "No path specified when installing app from local file" : "Nu s-a specificat vreo cale la instalarea aplicației de pe un fișier local", "Archives of type %s are not supported" : "Arhivele de tip %s nu sunt suportate", "Failed to open archive when installing app" : "Deschiderea arhivei a eșuat în timpul instalării aplicației", "App does not provide an info.xml file" : "Aplicația nu furnizează un fișier info.xml", @@ -51,10 +54,12 @@ OC.L10N.register( "DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"", "Offending command was: \"%s\"" : "Comanda cauză a fost: \"%s\"", "PostgreSQL username and/or password not valid" : "Nume utilizator și/sau parolă PostgreSQL greșită", + "For the best results, please consider using a GNU/Linux server instead." : "Pentru cele mai bune rezultate, ia în calcul folosirea unui server care rulează un sistem de operare GNU/Linux.", "Set an admin username." : "Setează un nume de administrator.", "Set an admin password." : "Setează o parolă de administrator.", "Invalid Federated Cloud ID" : "ID invalid cloud federalizat", "%s shared »%s« with you" : "%s Partajat »%s« cu tine de", + "%s via %s" : "%s via %s", "You are not allowed to share %s" : "Nu există permisiunea de partajare %s", "Sharing %s failed, because this item is already shared with %s" : "Partajarea %s a eșuat deoarece acest element este deja partajat cu %s", "Not allowed to create a federated share with the same user" : "Nu este permisă crearea unei partajări federalizate cu acelaşi utilizator", diff --git a/lib/l10n/ro.json b/lib/l10n/ro.json index 566f6d01123..9149650a015 100644 --- a/lib/l10n/ro.json +++ b/lib/l10n/ro.json @@ -27,6 +27,7 @@ "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulul cu id-ul %s nu există. Activează-l în setările tale de aplicație sau contactează-ți administratorul.", "Empty filename is not allowed" : "Nu este permis fișier fără nume", "Dot files are not allowed" : "Fișierele care încep cu caracterul punct nu sunt permise", + "4-byte characters are not supported in file names" : "Caracterele stocate în 4 octeți nu sunt suportate în denumirile fișierelor", "File name is a reserved word" : "Numele fișierului este un cuvânt rezervat", "File name contains at least one invalid character" : "Numele fișierului conține măcar un caracter invalid", "File name is too long" : "Numele fișierului este prea lung", @@ -34,6 +35,8 @@ "Can't create app folder. Please fix permissions. %s" : "Nu se poate crea directorul de aplicație. Repară permisiunile. %s", "Archive does not contain a directory named %s" : "Arhiva nu conține vreun director cu numele %s", "No source specified when installing app" : "Nu a fost specificată vreo sursă la instalarea aplicației", + "No href specified when installing app from http" : "Nu s-a specificat adresa la instalarea aplicației dintr-o sursă de pe Internet", + "No path specified when installing app from local file" : "Nu s-a specificat vreo cale la instalarea aplicației de pe un fișier local", "Archives of type %s are not supported" : "Arhivele de tip %s nu sunt suportate", "Failed to open archive when installing app" : "Deschiderea arhivei a eșuat în timpul instalării aplicației", "App does not provide an info.xml file" : "Aplicația nu furnizează un fișier info.xml", @@ -49,10 +52,12 @@ "DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"", "Offending command was: \"%s\"" : "Comanda cauză a fost: \"%s\"", "PostgreSQL username and/or password not valid" : "Nume utilizator și/sau parolă PostgreSQL greșită", + "For the best results, please consider using a GNU/Linux server instead." : "Pentru cele mai bune rezultate, ia în calcul folosirea unui server care rulează un sistem de operare GNU/Linux.", "Set an admin username." : "Setează un nume de administrator.", "Set an admin password." : "Setează o parolă de administrator.", "Invalid Federated Cloud ID" : "ID invalid cloud federalizat", "%s shared »%s« with you" : "%s Partajat »%s« cu tine de", + "%s via %s" : "%s via %s", "You are not allowed to share %s" : "Nu există permisiunea de partajare %s", "Sharing %s failed, because this item is already shared with %s" : "Partajarea %s a eșuat deoarece acest element este deja partajat cu %s", "Not allowed to create a federated share with the same user" : "Nu este permisă crearea unei partajări federalizate cu acelaşi utilizator", diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index 6e99e1ac268..e082cea3305 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -154,7 +154,7 @@ class AllConfig implements \OCP\IConfig { * * @param string $appName the appName that we want to store the value under * @param string $key the key of the value, under which will be saved - * @param string $value the value that should be stored + * @param string|float|int $value the value that should be stored */ public function setAppValue($appName, $key, $value) { \OC::$server->getAppConfig()->setValue($appName, $key, $value); @@ -198,11 +198,16 @@ class AllConfig implements \OCP\IConfig { * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we want to store the value under * @param string $key the key under which the value is being stored - * @param string $value the value that you want to store + * @param string|float|int $value the value that you want to store * @param string $preCondition only update if the config value was previously the value passed as $preCondition * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met + * @throws \UnexpectedValueException when trying to store an unexpected value */ public function setUserValue($userId, $appName, $key, $value, $preCondition = null) { + if (!is_int($value) && !is_float($value) && !is_string($value)) { + throw new \UnexpectedValueException('Only integers, floats and strings are allowed as value'); + } + // TODO - FIXME $this->fixDIInit(); diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index 24542152ffc..f84c8a41f17 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -143,7 +143,7 @@ class AppConfig implements IAppConfig { * * @param string $app app * @param string $key key - * @param string $value value + * @param string|float|int $value value * @return bool True if the value was inserted or updated, false if the value was the same */ public function setValue($app, $key, $value) { diff --git a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php index 2b9a072e59a..d84e9963436 100644 --- a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php @@ -26,13 +26,13 @@ namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; use OC\AppFramework\Utility\ControllerMethodReflector; +use OC\User\Session; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; -use OCP\IRequest; -use OCP\IUserSession; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; +use OCP\IRequest; /** * This middleware sets the correct CORS headers on a response if the @@ -53,18 +53,18 @@ class CORSMiddleware extends Middleware { private $reflector; /** - * @var IUserSession + * @var Session */ private $session; /** * @param IRequest $request * @param ControllerMethodReflector $reflector - * @param IUserSession $session + * @param Session $session */ public function __construct(IRequest $request, ControllerMethodReflector $reflector, - IUserSession $session) { + Session $session) { $this->request = $request; $this->reflector = $reflector; $this->session = $session; @@ -89,7 +89,7 @@ class CORSMiddleware extends Middleware { $pass = $this->request->server['PHP_AUTH_PW']; $this->session->logout(); - if(!$this->session->login($user, $pass)) { + if(!$this->session->logClientIn($user, $pass)) { throw new SecurityException('CORS requires basic auth', Http::STATUS_UNAUTHORIZED); } } diff --git a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php index 04b98c6c5a0..7746d6be915 100644 --- a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php +++ b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php @@ -28,6 +28,7 @@ class DefaultTokenCleanupJob extends Job { protected function run($argument) { /* @var $provider DefaultTokenProvider */ + // TODO: add OC\Authentication\Token\IProvider::invalidateOldTokens and query interface $provider = OC::$server->query('OC\Authentication\Token\DefaultTokenProvider'); $provider->invalidateOldTokens(); } diff --git a/lib/private/Encryption/DecryptAll.php b/lib/private/Encryption/DecryptAll.php index 8676bc09575..34a3e1bff91 100644 --- a/lib/private/Encryption/DecryptAll.php +++ b/lib/private/Encryption/DecryptAll.php @@ -80,7 +80,7 @@ class DecryptAll { $this->input = $input; $this->output = $output; - if (!empty($user) && $this->userManager->userExists($user) === false) { + if ($user !== '' && $this->userManager->userExists($user) === false) { $this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again'); return false; } @@ -141,7 +141,7 @@ class DecryptAll { $this->output->writeln("\n"); $userList = []; - if (empty($user)) { + if ($user === '') { $fetchUsersProgress = new ProgressBar($this->output); $fetchUsersProgress->setFormat(" %message% \n [%bar%]"); diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php index e6bd118d5a5..c17f9bfd51b 100644 --- a/lib/private/Files/Cache/Scanner.php +++ b/lib/private/Files/Cache/Scanner.php @@ -38,6 +38,7 @@ use OC\Files\Filesystem; use OC\Hooks\BasicEmitter; use OCP\Config; use OCP\Files\Cache\IScanner; +use OCP\Files\ForbiddenException; use OCP\Files\Storage\ILockingStorage; use OCP\Lock\ILockingProvider; @@ -140,7 +141,11 @@ class Scanner extends BasicEmitter implements IScanner { } } - $data = $this->getData($file); + try { + $data = $this->getData($file); + } catch (ForbiddenException $e) { + return null; + } if ($data) { diff --git a/lib/private/Files/Cache/Updater.php b/lib/private/Files/Cache/Updater.php index 820941abae1..4e17c4d778d 100644 --- a/lib/private/Files/Cache/Updater.php +++ b/lib/private/Files/Cache/Updater.php @@ -231,7 +231,10 @@ class Updater implements IUpdater { $parentId = $this->cache->getParentId($internalPath); $parent = dirname($internalPath); if ($parentId != -1) { - $this->cache->update($parentId, array('storage_mtime' => $this->storage->filemtime($parent))); + $mtime = $this->storage->filemtime($parent); + if ($mtime !== false) { + $this->cache->update($parentId, array('storage_mtime' => $mtime)); + } } } } diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index cec6a42a2c0..0c1b69108d4 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -643,6 +643,9 @@ abstract class Common implements Storage, ILockingStorage { $data = []; $data['mimetype'] = $this->getMimeType($path); $data['mtime'] = $this->filemtime($path); + if ($data['mtime'] === false) { + $data['mtime'] = time(); + } if ($data['mimetype'] == 'httpd/unix-directory') { $data['size'] = -1; //unknown } else { diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index b07e26a3358..005b5f9ab91 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -33,20 +33,31 @@ */ namespace OC\Files\Storage; + +use OCP\Files\ForbiddenException; + /** * for local filestore, we only have to map the paths */ class Local extends \OC\Files\Storage\Common { protected $datadir; + protected $dataDirLength; + + protected $allowSymlinks = false; + + protected $realDataDir; + public function __construct($arguments) { if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) { throw new \InvalidArgumentException('No data directory set for local storage'); } $this->datadir = $arguments['datadir']; + $this->realDataDir = rtrim(realpath($this->datadir), '/') . '/'; if (substr($this->datadir, -1) !== '/') { $this->datadir .= '/'; } + $this->dataDirLength = strlen($this->realDataDir); } public function __destruct() { @@ -157,7 +168,7 @@ class Local extends \OC\Files\Storage\Common { public function filemtime($path) { clearstatcache($this->getSourcePath($path)); - return filemtime($this->getSourcePath($path)); + return $this->file_exists($path) ? filemtime($this->getSourcePath($path)) : false; } public function touch($path, $mtime = null) { @@ -188,7 +199,7 @@ class Local extends \OC\Files\Storage\Common { return ''; } - $handle = fopen($fileName,'rb'); + $handle = fopen($fileName, 'rb'); $content = fread($handle, $fileSize); fclose($handle); return $content; @@ -337,10 +348,27 @@ class Local extends \OC\Files\Storage\Common { * * @param string $path * @return string + * @throws ForbiddenException */ public function getSourcePath($path) { $fullPath = $this->datadir . $path; - return $fullPath; + if ($this->allowSymlinks || $path === '') { + return $fullPath; + } + $pathToResolve = $fullPath; + $realPath = realpath($pathToResolve); + while ($realPath === false) { // for non existing files check the parent directory + $pathToResolve = dirname($pathToResolve); + $realPath = realpath($pathToResolve); + } + if ($realPath) { + $realPath = $realPath . '/'; + } + if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) { + return $fullPath; + } else { + throw new ForbiddenException("Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", false); + } } /** @@ -377,7 +405,7 @@ class Local extends \OC\Files\Storage\Common { * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { - if($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')){ + if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')) { /** * @var \OC\Files\Storage\Local $sourceStorage */ diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php index 9b55c312e26..8beba116fe1 100644 --- a/lib/private/Files/Utils/Scanner.php +++ b/lib/private/Files/Utils/Scanner.php @@ -160,7 +160,12 @@ class Scanner extends PublicEmitter { if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and (!$storage->isCreatable('') or !$storage->isCreatable('files')) ) { - throw new ForbiddenException(); + if ($storage->file_exists('') or $storage->getCache()->inCache('')) { + throw new ForbiddenException(); + } else {// if the root exists in neither the cache nor the storage the user isn't setup yet + break; + } + } $relativePath = $mount->getInternalPath($dir); $scanner = $storage->getScanner(); diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index f738542ea8c..e9daa123470 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -337,10 +337,17 @@ class View { return $this->removeMount($mount, $absolutePath); } if ($this->is_dir($path)) { - return $this->basicOperation('rmdir', $path, array('delete')); + $result = $this->basicOperation('rmdir', $path, array('delete')); } else { - return false; + $result = false; + } + + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($absolutePath); + $storage->getUpdater()->remove($internalPath); } + return $result; } /** @@ -429,7 +436,7 @@ class View { /** * @param string $path - * @param int $from + * @param int $from * @param int $to * @return bool|mixed * @throws \OCP\Files\InvalidPathException @@ -441,18 +448,18 @@ class View { $handle = $this->fopen($path, 'rb'); if ($handle) { if (fseek($handle, $from) === 0) { - $chunkSize = 8192; // 8 kB chunks - $end = $to + 1; - while (!feof($handle) && ftell($handle) < $end) { - $len = $end-ftell($handle); - if ($len > $chunkSize) { - $len = $chunkSize; + $chunkSize = 8192; // 8 kB chunks + $end = $to + 1; + while (!feof($handle) && ftell($handle) < $end) { + $len = $end - ftell($handle); + if ($len > $chunkSize) { + $len = $chunkSize; + } + echo fread($handle, $len); + flush(); } - echo fread($handle, $len); - flush(); - } - $size = ftell($handle) - $from; - return $size; + $size = ftell($handle) - $from; + return $size; } throw new \OCP\Files\UnseekableException('fseek error'); @@ -679,7 +686,15 @@ class View { if ($mount and $mount->getInternalPath($absolutePath) === '') { return $this->removeMount($mount, $absolutePath); } - return $this->basicOperation('unlink', $path, array('delete')); + $result = $this->basicOperation('unlink', $path, array('delete')); + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($absolutePath); + $storage->getUpdater()->remove($internalPath); + return true; + } else { + return $result; + } } /** diff --git a/lib/private/Log.php b/lib/private/Log.php index 6028064a878..49223521916 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -60,6 +60,32 @@ class Log implements ILogger { /** @var Normalizer */ private $normalizer; + protected $methodsWithSensitiveParameters = [ + // Session/User + 'login', + 'checkPassword', + 'updatePrivateKeyPassword', + 'validateUserPass', + + // TokenProvider + 'getToken', + 'isTokenPassword', + 'getPassword', + 'decryptPassword', + 'logClientIn', + 'generateToken', + 'validateToken', + + // TwoFactorAuth + 'solveChallenge', + 'verifyChallenge', + + //ICrypto + 'calculateHMAC', + 'encrypt', + 'decrypt', + ]; + /** * @param string $logger The logger that should be used * @param SystemConfig $config the system config object @@ -286,7 +312,7 @@ class Log implements ILogger { 'File' => $exception->getFile(), 'Line' => $exception->getLine(), ); - $exception['Trace'] = preg_replace('!(login|checkPassword|updatePrivateKeyPassword|validateUserPass)\(.*\)!', '$1(*** username and password replaced ***)', $exception['Trace']); + $exception['Trace'] = preg_replace('!(' . implode('|', $this->methodsWithSensitiveParameters) . ')\(.*\)!', '$1(*** sensitive parameters replaced ***)', $exception['Trace']); $msg = isset($context['message']) ? $context['message'] : 'Exception'; $msg .= ': ' . json_encode($exception); $this->error($msg, $context); diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index 362468d4109..f560bb4bfc0 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -361,7 +361,14 @@ class Session implements IUserSession, Emitter { // TODO: throw LoginException instead (https://github.com/owncloud/core/pull/24616) return false; } - return $this->login($user, $password); + if (!$this->login($user, $password) ) { + $users = $this->manager->getByEmail($user); + if (count($users) === 1) { + return $this->login($users[0]->getUID(), $password); + } + return false; + } + return true; } private function isTokenAuthEnforced() { @@ -376,7 +383,11 @@ class Session implements IUserSession, Emitter { ); $user = $this->manager->get($username); if (is_null($user)) { - return true; + $users = $this->manager->getByEmail($username); + if (count($users) !== 1) { + return true; + } + $user = $users[0]; } // DI not possible due to cyclic dependencies :'-/ return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user); @@ -385,7 +396,7 @@ class Session implements IUserSession, Emitter { /** * Check if the given 'password' is actually a device token * - * @param type $password + * @param string $password * @return boolean */ public function isTokenPassword($password) { @@ -470,11 +481,39 @@ class Session implements IUserSession, Emitter { $name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser'; try { $sessionId = $this->session->getId(); - $this->tokenProvider->generateToken($sessionId, $uid, $loginName, $password, $name); + $pwd = $this->getPassword($password); + $this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name); + return true; } catch (SessionNotAvailableException $ex) { + // This can happen with OCC, where a memory session is used + // if a memory session is used, we shouldn't create a session token anyway + return false; + } + } + /** + * Checks if the given password is a token. + * If yes, the password is extracted from the token. + * If no, the same password is returned. + * + * @param string $password either the login password or a device token + * @return string|null the password or null if none was set in the token + */ + private function getPassword($password) { + if (is_null($password)) { + // This is surely no token ;-) + return null; + } + try { + $token = $this->tokenProvider->getToken($password); + try { + return $this->tokenProvider->getPassword($token, $password); + } catch (PasswordlessTokenException $ex) { + return null; + } + } catch (InvalidTokenException $ex) { + return $password; } - return true; } /** diff --git a/lib/private/legacy/api.php b/lib/private/legacy/api.php index 1e581153ce6..024f3c0fb63 100644 --- a/lib/private/legacy/api.php +++ b/lib/private/legacy/api.php @@ -349,7 +349,7 @@ class OC_API { if ($ocsApiRequest) { // initialize the user's filesystem - \OC_Util::setUpFS(\OC_User::getUser()); + \OC_Util::setupFS(\OC_User::getUser()); self::$isLoggedIn = true; return OC_User::getUser(); @@ -374,7 +374,7 @@ class OC_API { self::$logoutRequired = true; // initialize the user's filesystem - \OC_Util::setUpFS(\OC_User::getUser()); + \OC_Util::setupFS(\OC_User::getUser()); self::$isLoggedIn = true; return \OC_User::getUser(); diff --git a/lib/private/legacy/helper.php b/lib/private/legacy/helper.php index f107d47faf7..21fb3cbc5ab 100644 --- a/lib/private/legacy/helper.php +++ b/lib/private/legacy/helper.php @@ -206,7 +206,9 @@ class OC_Helper { foreach ($files as $fileInfo) { /** @var SplFileInfo $fileInfo */ - if ($fileInfo->isDir()) { + if ($fileInfo->isLink()) { + unlink($fileInfo->getPathname()); + } else if ($fileInfo->isDir()) { rmdir($fileInfo->getRealPath()); } else { unlink($fileInfo->getRealPath()); diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index b744db21238..a863348566e 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -1128,19 +1128,8 @@ class OC_Util { return $encoded; } - /** - * Check if the .htaccess file is working - * @param \OCP\IConfig $config - * @return bool - * @throws Exception - * @throws \OC\HintException If the test file can't get written. - */ - public function isHtaccessWorking(\OCP\IConfig $config) { - - if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) { - return true; - } + public function createHtaccessTestFile(\OCP\IConfig $config) { // php dev server does not support htaccess if (php_sapi_name() === 'cli-server') { return false; @@ -1148,7 +1137,7 @@ class OC_Util { // testdata $fileName = '/htaccesstest.txt'; - $testContent = 'testcontent'; + $testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.'; // creating a test file $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName; @@ -1164,6 +1153,28 @@ class OC_Util { } fwrite($fp, $testContent); fclose($fp); + } + + /** + * Check if the .htaccess file is working + * @param \OCP\IConfig $config + * @return bool + * @throws Exception + * @throws \OC\HintException If the test file can't get written. + */ + public function isHtaccessWorking(\OCP\IConfig $config) { + + if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) { + return true; + } + + $testContent = $this->createHtaccessTestFile($config); + if ($testContent === false) { + return false; + } + + $fileName = '/htaccesstest.txt'; + $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName; // accessing the file via http $url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName); diff --git a/lib/public/IAppConfig.php b/lib/public/IAppConfig.php index 1406e8a56d6..22fcdbbb205 100644 --- a/lib/public/IAppConfig.php +++ b/lib/public/IAppConfig.php @@ -88,7 +88,7 @@ interface IAppConfig { * sets a value in the appconfig * @param string $app app * @param string $key key - * @param string $value value + * @param string|float|int $value value * @deprecated 8.0.0 use method setAppValue of \OCP\IConfig * * Sets a value. If the key did not exist before it will be created. diff --git a/lib/public/IConfig.php b/lib/public/IConfig.php index 9e5024545b3..05158e9063e 100644 --- a/lib/public/IConfig.php +++ b/lib/public/IConfig.php @@ -104,7 +104,7 @@ interface IConfig { * Writes a new app wide value * * @param string $appName the appName that we want to store the value under - * @param string $key the key of the value, under which will be saved + * @param string|float|int $key the key of the value, under which will be saved * @param string $value the value that should be stored * @return void * @since 6.0.0 @@ -149,6 +149,7 @@ interface IConfig { * @param string $value the value that you want to store * @param string $preCondition only update if the config value was previously the value passed as $preCondition * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met + * @throws \UnexpectedValueException when trying to store an unexpected value * @since 6.0.0 - parameter $precondition was added in 8.0.0 */ public function setUserValue($userId, $appName, $key, $value, $preCondition = null); diff --git a/settings/admin.php b/settings/admin.php index 6fb65b013e6..3ae7455b2ea 100644 --- a/settings/admin.php +++ b/settings/admin.php @@ -267,3 +267,7 @@ if ($updaterAppPanel) { $template->assign('forms', $formsAndMore); $template->printPage(); + +$util = new \OC_Util(); +$util->createHtaccessTestFile(\OC::$server->getConfig()); + diff --git a/settings/js/users/users.js b/settings/js/users/users.js index 03f471bc3b4..e0fccbd9539 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -14,7 +14,7 @@ var UserList = { availableGroups: [], offset: 0, usersToLoad: 10, //So many users will be loaded when user scrolls down - initialUsersToLoad: 250, //initial number of users to load + initialUsersToLoad: 50, //initial number of users to load currentGid: '', filter: '', diff --git a/settings/l10n/pl.js b/settings/l10n/pl.js index 8c592ec5114..7f14e552192 100644 --- a/settings/l10n/pl.js +++ b/settings/l10n/pl.js @@ -6,6 +6,7 @@ OC.L10N.register( "Authentication error" : "Błąd uwierzytelniania", "Please provide an admin recovery password, otherwise all user data will be lost" : "Podaj hasło odzyskiwania administratora, w przeciwnym razie wszystkie dane użytkownika zostaną utracone", "Wrong admin recovery password. Please check the password and try again." : "Błędne hasło odzyskiwania. Sprawdź hasło i spróbuj ponownie.", + "Backend doesn't support password change, but the user's encryption key was successfully updated." : "Zaplecze nie obsługuje zmiany hasła, ale klucz szyfrowania użytkownika został pomyślnie zaktualizowany.", "Unable to change password" : "Nie można zmienić hasła", "Enabled" : "Włączone", "Not enabled" : "Nie włączone", diff --git a/settings/l10n/pl.json b/settings/l10n/pl.json index ded5e82af0a..6ab582825f7 100644 --- a/settings/l10n/pl.json +++ b/settings/l10n/pl.json @@ -4,6 +4,7 @@ "Authentication error" : "Błąd uwierzytelniania", "Please provide an admin recovery password, otherwise all user data will be lost" : "Podaj hasło odzyskiwania administratora, w przeciwnym razie wszystkie dane użytkownika zostaną utracone", "Wrong admin recovery password. Please check the password and try again." : "Błędne hasło odzyskiwania. Sprawdź hasło i spróbuj ponownie.", + "Backend doesn't support password change, but the user's encryption key was successfully updated." : "Zaplecze nie obsługuje zmiany hasła, ale klucz szyfrowania użytkownika został pomyślnie zaktualizowany.", "Unable to change password" : "Nie można zmienić hasła", "Enabled" : "Włączone", "Not enabled" : "Nie włączone", diff --git a/settings/l10n/ro.js b/settings/l10n/ro.js index 4f965ce5b74..8afe595859a 100644 --- a/settings/l10n/ro.js +++ b/settings/l10n/ro.js @@ -48,8 +48,16 @@ OC.L10N.register( "Unable to add user to group %s" : "Nu s-a putut adăuga utilizatorul în grupul %s", "Unable to remove user from group %s" : "Nu s-a putut elimina utilizatorul din grupul %s", "Couldn't update app." : "Aplicaţia nu s-a putut actualiza.", + "Add trusted domain" : "Adaugă domeniu de încredere", + "Migration in progress. Please wait until the migration is finished" : "Migrare în progres. Așteaptă până când migrarea este finalizată", + "Migration started …" : "Migrarea a început...", "Sending..." : "Se expediază...", + "Official" : "Oficial", + "Approved" : "Aprobat", + "Experimental" : "Experimental", "All" : "Toate ", + "No apps found for your version" : "Nu au fost găsite aplicații pentru versiunea ta", + "The app will be downloaded from the app store" : "Aplicația va fi descărcată din magazin", "Please wait...." : "Aşteptaţi vă rog....", "Error while disabling app" : "Eroare în timpul dezactivării aplicației", "Disable" : "Dezactivați", @@ -60,7 +68,9 @@ OC.L10N.register( "Updated" : "Actualizat", "Uninstalling ...." : "Dezinstalaza ....", "Uninstall" : "Dezinstalați", + "Valid until {date}" : "Valabil până la {date}", "Delete" : "Șterge", + "An error occurred: {message}" : "A apărut o eroare: {message}", "Select a profile picture" : "Selectează o imagine de profil", "Very weak password" : "Parolă foarte slabă", "Weak password" : "Parolă slabă", @@ -68,13 +78,21 @@ OC.L10N.register( "Good password" : "Parolă bună", "Strong password" : "Parolă puternică", "Groups" : "Grupuri", + "Unable to delete {objName}" : "Nu s-a putut șterge {objName}", + "deleted {groupName}" : "{groupName} s-a șters", "undo" : "Anulează ultima acțiune", + "no group" : "niciun grup", "never" : "niciodată", + "deleted {userName}" : "{userName} șters", "add group" : "adăugaţi grupul", "A valid username must be provided" : "Trebuie să furnizaţi un nume de utilizator valid", "A valid password must be provided" : "Trebuie să furnizaţi o parolă validă", "__language_name__" : "_language_name_", "Unlimited" : "Nelimitată", + "Personal info" : "Informații personale", + "Sessions" : "Sesiuni", + "Devices" : "Dispozitive", + "Sync clients" : "Sincronizează clienții", "None" : "Niciuna", "Login" : "Autentificare", "SSL" : "SSL", @@ -83,31 +101,75 @@ OC.L10N.register( "Open documentation" : "Deschide documentația", "Allow apps to use the Share API" : "Permite aplicațiilor să folosească API-ul de partajare", "Allow public uploads" : "Permite încărcări publice", + "Enforce password protection" : "Impune protecția prin parolă", + "Set default expiration date" : "Setează data implicită de expirare", "Allow users to send mail notification for shared files" : "Permite utilizatorilor sa expedieze notificări prin e-mail pentru dosarele comune", + "Expire after " : "Expiră după", "days" : "zile", + "Enforce expiration date" : "Impune data de expirare", "Allow resharing" : "Permite repartajarea", + "Allow sharing with groups" : "Permite partajarea cu grupuri", + "Exclude groups from sharing" : "Exclude grupuri de la partajare", "Execute one task with each page loaded" : "Execută o sarcină la fiecare pagină încărcată", + "Enable server-side encryption" : "Activează criptarea pe server", + "Please read carefully before activating server-side encryption: " : "Citește cu atenție înainte să activezi criptarea pe server:", + "This is the final warning: Do you really want to enable encryption?" : "Aceasta este avertizarea finală: Chiar vrei să activezi criptarea?", "Enable encryption" : "Activează criptarea", + "Select default encryption module:" : "Selectează modulul implicit de criptare:", + "Start migration" : "Pornește migrarea", "Send mode" : "Modul de expediere", "Encryption" : "Încriptare", + "From address" : "De la adresa", + "mail" : "poștă", "Authentication method" : "Modul de autentificare", "Authentication required" : "Autentificare necesară", "Server address" : "Adresa server-ului", "Port" : "Portul", + "Credentials" : "Detalii de autentificare", "SMTP Username" : "Nume utilizator SMTP", "SMTP Password" : "Parolă SMTP", + "Store credentials" : "Stochează datele de autentificare", "Test email settings" : "Verifică setările de e-mail", "Send email" : "Expediază mesajul", + "Download logfile" : "Descarcă fișierul cu loguri", "More" : "Mai mult", "Less" : "Mai puțin", + "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Fișierul cu loguri este mai mare de 100 MB. Descărcarea acestuia ar putea dura ceva timp!", + "What to log" : "Ce să loghezi", + "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite e folosit ca o bază de date. Pentru instalări mari recomandăm folosirea unei alte baze de date.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "În special atunci când este folosit clientul desktop pentru sincronizarea fișierelor, utilizarea SQLite este nerecomandată.", + "How to do backups" : "Cum să faci copii de rezervă", + "Advanced monitoring" : "Monitorizare avansată", "Version" : "Versiunea", + "Developer documentation" : "Documentație pentru dezvoltatori", + "by %s" : "de %s", + "%s-licensed" : "%s-licențiat", + "Documentation:" : "Documentație:", + "User documentation" : "Documentație utilizator", + "Admin documentation" : "Documentație pentru administrare", + "Show description …" : "Arată descriere ...", + "Hide description …" : "Ascunde descriere ...", + "This app has an update available." : "Este disponibilă o actualizare pentru această aplicație.", + "Enable only for specific groups" : "Activează doar pentru grupuri specifice", + "Uninstall App" : "Dezinstalează aplicația", + "Enable experimental apps" : "Activează aplicațiile experimentale", + "SSL Root Certificates" : "Certificate SSL rădăcină", + "Common Name" : "Nume comun", + "Valid until" : "Valabil până la", + "Issued By" : "Emis de", + "Valid until %s" : "Valabil până la %s", + "Import root certificate" : "Importă certificat rădăcină", "Cheers!" : "Noroc!", + "Administrator documentation" : "Documentație pentru administrare", + "Online documentation" : "Documentație online", "Forum" : "Forum", + "Commercial support" : "Suport comercial", "Profile picture" : "Imagine de profil", "Upload new" : "Încarcă una nouă", "Remove image" : "Înlătură imagine", + "png or jpg, max. 20 MB" : "png sau jpg, max. 20 MB", "Cancel" : "Anulare", + "Full name" : "Nume complet", "Email" : "Email", "Your email address" : "Adresa ta de email", "Password" : "Parolă", @@ -115,6 +177,8 @@ OC.L10N.register( "Current password" : "Parola curentă", "New password" : "Noua parolă", "Change password" : "Schimbă parola", + "Most recent activity" : "Cea mai recentă activitate", + "You've linked these devices." : "Ai legat aceste dispozitive.", "Name" : "Nume", "Language" : "Limba", "Help translate" : "Ajută la traducere", @@ -128,12 +192,16 @@ OC.L10N.register( "Admin Recovery Password" : "Parolă de recuperare a Administratorului", "Enter the recovery password in order to recover the users files during password change" : "Introdu parola de recuperare pentru a recupera fișierele utilizatorilor în timpul schimbării parolei", "Group" : "Grup", + "Admins" : "Administratori", "Default Quota" : "Cotă implicită", "Other" : "Altele", "Full Name" : "Nume complet", + "Group Admin for" : "Administrator de grup pentru", "Quota" : "Cotă", + "Last Login" : "Ultima autentificare", "change full name" : "schimbă numele complet", "set new password" : "setează parolă nouă", + "change email address" : "schimbă adresa email", "Default" : "Implicită" }, "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"); diff --git a/settings/l10n/ro.json b/settings/l10n/ro.json index 05540e9da90..8682709a39e 100644 --- a/settings/l10n/ro.json +++ b/settings/l10n/ro.json @@ -46,8 +46,16 @@ "Unable to add user to group %s" : "Nu s-a putut adăuga utilizatorul în grupul %s", "Unable to remove user from group %s" : "Nu s-a putut elimina utilizatorul din grupul %s", "Couldn't update app." : "Aplicaţia nu s-a putut actualiza.", + "Add trusted domain" : "Adaugă domeniu de încredere", + "Migration in progress. Please wait until the migration is finished" : "Migrare în progres. Așteaptă până când migrarea este finalizată", + "Migration started …" : "Migrarea a început...", "Sending..." : "Se expediază...", + "Official" : "Oficial", + "Approved" : "Aprobat", + "Experimental" : "Experimental", "All" : "Toate ", + "No apps found for your version" : "Nu au fost găsite aplicații pentru versiunea ta", + "The app will be downloaded from the app store" : "Aplicația va fi descărcată din magazin", "Please wait...." : "Aşteptaţi vă rog....", "Error while disabling app" : "Eroare în timpul dezactivării aplicației", "Disable" : "Dezactivați", @@ -58,7 +66,9 @@ "Updated" : "Actualizat", "Uninstalling ...." : "Dezinstalaza ....", "Uninstall" : "Dezinstalați", + "Valid until {date}" : "Valabil până la {date}", "Delete" : "Șterge", + "An error occurred: {message}" : "A apărut o eroare: {message}", "Select a profile picture" : "Selectează o imagine de profil", "Very weak password" : "Parolă foarte slabă", "Weak password" : "Parolă slabă", @@ -66,13 +76,21 @@ "Good password" : "Parolă bună", "Strong password" : "Parolă puternică", "Groups" : "Grupuri", + "Unable to delete {objName}" : "Nu s-a putut șterge {objName}", + "deleted {groupName}" : "{groupName} s-a șters", "undo" : "Anulează ultima acțiune", + "no group" : "niciun grup", "never" : "niciodată", + "deleted {userName}" : "{userName} șters", "add group" : "adăugaţi grupul", "A valid username must be provided" : "Trebuie să furnizaţi un nume de utilizator valid", "A valid password must be provided" : "Trebuie să furnizaţi o parolă validă", "__language_name__" : "_language_name_", "Unlimited" : "Nelimitată", + "Personal info" : "Informații personale", + "Sessions" : "Sesiuni", + "Devices" : "Dispozitive", + "Sync clients" : "Sincronizează clienții", "None" : "Niciuna", "Login" : "Autentificare", "SSL" : "SSL", @@ -81,31 +99,75 @@ "Open documentation" : "Deschide documentația", "Allow apps to use the Share API" : "Permite aplicațiilor să folosească API-ul de partajare", "Allow public uploads" : "Permite încărcări publice", + "Enforce password protection" : "Impune protecția prin parolă", + "Set default expiration date" : "Setează data implicită de expirare", "Allow users to send mail notification for shared files" : "Permite utilizatorilor sa expedieze notificări prin e-mail pentru dosarele comune", + "Expire after " : "Expiră după", "days" : "zile", + "Enforce expiration date" : "Impune data de expirare", "Allow resharing" : "Permite repartajarea", + "Allow sharing with groups" : "Permite partajarea cu grupuri", + "Exclude groups from sharing" : "Exclude grupuri de la partajare", "Execute one task with each page loaded" : "Execută o sarcină la fiecare pagină încărcată", + "Enable server-side encryption" : "Activează criptarea pe server", + "Please read carefully before activating server-side encryption: " : "Citește cu atenție înainte să activezi criptarea pe server:", + "This is the final warning: Do you really want to enable encryption?" : "Aceasta este avertizarea finală: Chiar vrei să activezi criptarea?", "Enable encryption" : "Activează criptarea", + "Select default encryption module:" : "Selectează modulul implicit de criptare:", + "Start migration" : "Pornește migrarea", "Send mode" : "Modul de expediere", "Encryption" : "Încriptare", + "From address" : "De la adresa", + "mail" : "poștă", "Authentication method" : "Modul de autentificare", "Authentication required" : "Autentificare necesară", "Server address" : "Adresa server-ului", "Port" : "Portul", + "Credentials" : "Detalii de autentificare", "SMTP Username" : "Nume utilizator SMTP", "SMTP Password" : "Parolă SMTP", + "Store credentials" : "Stochează datele de autentificare", "Test email settings" : "Verifică setările de e-mail", "Send email" : "Expediază mesajul", + "Download logfile" : "Descarcă fișierul cu loguri", "More" : "Mai mult", "Less" : "Mai puțin", + "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Fișierul cu loguri este mai mare de 100 MB. Descărcarea acestuia ar putea dura ceva timp!", + "What to log" : "Ce să loghezi", + "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite e folosit ca o bază de date. Pentru instalări mari recomandăm folosirea unei alte baze de date.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "În special atunci când este folosit clientul desktop pentru sincronizarea fișierelor, utilizarea SQLite este nerecomandată.", + "How to do backups" : "Cum să faci copii de rezervă", + "Advanced monitoring" : "Monitorizare avansată", "Version" : "Versiunea", + "Developer documentation" : "Documentație pentru dezvoltatori", + "by %s" : "de %s", + "%s-licensed" : "%s-licențiat", + "Documentation:" : "Documentație:", + "User documentation" : "Documentație utilizator", + "Admin documentation" : "Documentație pentru administrare", + "Show description …" : "Arată descriere ...", + "Hide description …" : "Ascunde descriere ...", + "This app has an update available." : "Este disponibilă o actualizare pentru această aplicație.", + "Enable only for specific groups" : "Activează doar pentru grupuri specifice", + "Uninstall App" : "Dezinstalează aplicația", + "Enable experimental apps" : "Activează aplicațiile experimentale", + "SSL Root Certificates" : "Certificate SSL rădăcină", + "Common Name" : "Nume comun", + "Valid until" : "Valabil până la", + "Issued By" : "Emis de", + "Valid until %s" : "Valabil până la %s", + "Import root certificate" : "Importă certificat rădăcină", "Cheers!" : "Noroc!", + "Administrator documentation" : "Documentație pentru administrare", + "Online documentation" : "Documentație online", "Forum" : "Forum", + "Commercial support" : "Suport comercial", "Profile picture" : "Imagine de profil", "Upload new" : "Încarcă una nouă", "Remove image" : "Înlătură imagine", + "png or jpg, max. 20 MB" : "png sau jpg, max. 20 MB", "Cancel" : "Anulare", + "Full name" : "Nume complet", "Email" : "Email", "Your email address" : "Adresa ta de email", "Password" : "Parolă", @@ -113,6 +175,8 @@ "Current password" : "Parola curentă", "New password" : "Noua parolă", "Change password" : "Schimbă parola", + "Most recent activity" : "Cea mai recentă activitate", + "You've linked these devices." : "Ai legat aceste dispozitive.", "Name" : "Nume", "Language" : "Limba", "Help translate" : "Ajută la traducere", @@ -126,12 +190,16 @@ "Admin Recovery Password" : "Parolă de recuperare a Administratorului", "Enter the recovery password in order to recover the users files during password change" : "Introdu parola de recuperare pentru a recupera fișierele utilizatorilor în timpul schimbării parolei", "Group" : "Grup", + "Admins" : "Administratori", "Default Quota" : "Cotă implicită", "Other" : "Altele", "Full Name" : "Nume complet", + "Group Admin for" : "Administrator de grup pentru", "Quota" : "Cotă", + "Last Login" : "Ultima autentificare", "change full name" : "schimbă numele complet", "set new password" : "setează parolă nouă", + "change email address" : "schimbă adresa email", "Default" : "Implicită" },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" }
\ No newline at end of file diff --git a/tests/Core/Controller/TokenControllerTest.php b/tests/Core/Controller/TokenControllerTest.php index 386140a8a4f..b6b54b14fad 100644 --- a/tests/Core/Controller/TokenControllerTest.php +++ b/tests/Core/Controller/TokenControllerTest.php @@ -23,8 +23,9 @@ namespace Tests\Core\Controller; use OC\AppFramework\Http; +use OC\Authentication\Token\IToken; use OC\Core\Controller\TokenController; -use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\JSONResponse; use Test\TestCase; class TokenControllerTest extends TestCase { @@ -34,6 +35,7 @@ class TokenControllerTest extends TestCase { private $request; private $userManager; private $tokenProvider; + private $twoFactorAuthManager; private $secureRandom; protected function setUp() { @@ -43,17 +45,17 @@ class TokenControllerTest extends TestCase { $this->userManager = $this->getMockBuilder('\OC\User\Manager') ->disableOriginalConstructor() ->getMock(); - $this->tokenProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider') + $this->tokenProvider = $this->getMock('\OC\Authentication\Token\IProvider'); + $this->twoFactorAuthManager = $this->getMockBuilder('\OC\Authentication\TwoFactorAuth\Manager') ->disableOriginalConstructor() ->getMock(); $this->secureRandom = $this->getMock('\OCP\Security\ISecureRandom'); - $this->tokenController = new TokenController('core', $this->request, $this->userManager, $this->tokenProvider, - $this->secureRandom); + $this->tokenController = new TokenController('core', $this->request, $this->userManager, $this->tokenProvider, $this->twoFactorAuthManager, $this->secureRandom); } public function testWithoutCredentials() { - $expected = new Response(); + $expected = new JSONResponse(); $expected->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY); $actual = $this->tokenController->generateToken(null, null); @@ -66,7 +68,7 @@ class TokenControllerTest extends TestCase { ->method('checkPassword') ->with('john', 'passme') ->will($this->returnValue(false)); - $expected = new Response(); + $expected = new JSONResponse(); $expected->setStatus(Http::STATUS_UNAUTHORIZED); $actual = $this->tokenController->generateToken('john', 'passme'); @@ -83,13 +85,17 @@ class TokenControllerTest extends TestCase { $user->expects($this->once()) ->method('getUID') ->will($this->returnValue('john')); + $this->twoFactorAuthManager->expects($this->once()) + ->method('isTwoFactorAuthenticated') + ->with($user) + ->will($this->returnValue(false)); $this->secureRandom->expects($this->once()) ->method('generate') ->with(128) ->will($this->returnValue('verysecurerandomtoken')); $this->tokenProvider->expects($this->once()) ->method('generateToken') - ->with('verysecurerandomtoken', 'john', 'john', '123456', 'unknown client', \OC\Authentication\Token\IToken::PERMANENT_TOKEN); + ->with('verysecurerandomtoken', 'john', 'john', '123456', 'unknown client', IToken::PERMANENT_TOKEN); $expected = [ 'token' => 'verysecurerandomtoken' ]; @@ -99,4 +105,24 @@ class TokenControllerTest extends TestCase { $this->assertEquals($expected, $actual); } + public function testWithValidCredentialsBut2faEnabled() { + $user = $this->getMock('\OCP\IUser'); + $this->userManager->expects($this->once()) + ->method('checkPassword') + ->with('john', '123456') + ->will($this->returnValue($user)); + $this->twoFactorAuthManager->expects($this->once()) + ->method('isTwoFactorAuthenticated') + ->with($user) + ->will($this->returnValue(true)); + $this->secureRandom->expects($this->never()) + ->method('generate'); + $expected = new JSONResponse(); + $expected->setStatus(Http::STATUS_UNAUTHORIZED); + + $actual = $this->tokenController->generateToken('john', '123456'); + + $this->assertEquals($expected, $actual); + } + } diff --git a/tests/Core/Controller/TwoFactorChallengeControllerTest.php b/tests/Core/Controller/TwoFactorChallengeControllerTest.php index 2da6dcd52ac..08d8dd1452c 100644 --- a/tests/Core/Controller/TwoFactorChallengeControllerTest.php +++ b/tests/Core/Controller/TwoFactorChallengeControllerTest.php @@ -33,7 +33,7 @@ class TwoFactorChallengeControllerTest extends TestCase { private $session; private $urlGenerator; - /** TwoFactorChallengeController */ + /** @var TwoFactorChallengeController|\PHPUnit_Framework_MockObject_MockObject */ private $controller; protected function setUp() { @@ -47,9 +47,20 @@ class TwoFactorChallengeControllerTest extends TestCase { $this->session = $this->getMock('\OCP\ISession'); $this->urlGenerator = $this->getMock('\OCP\IURLGenerator'); - $this->controller = new TwoFactorChallengeController( - 'core', $this->request, $this->twoFactorManager, $this->userSession, $this->session, $this->urlGenerator - ); + $this->controller = $this->getMockBuilder('OC\Core\Controller\TwoFactorChallengeController') + ->setConstructorArgs([ + 'core', + $this->request, + $this->twoFactorManager, + $this->userSession, + $this->session, + $this->urlGenerator, + ]) + ->setMethods(['getLogoutAttribute']) + ->getMock(); + $this->controller->expects($this->any()) + ->method('getLogoutAttribute') + ->willReturn('logoutAttribute'); } public function testSelectChallenge() { @@ -70,6 +81,7 @@ class TwoFactorChallengeControllerTest extends TestCase { $expected = new \OCP\AppFramework\Http\TemplateResponse('core', 'twofactorselectchallenge', [ 'providers' => $providers, 'redirect_url' => '/some/url', + 'logout_attribute' => 'logoutAttribute', ], 'guest'); $this->assertEquals($expected, $this->controller->selectChallenge('/some/url')); @@ -110,6 +122,7 @@ class TwoFactorChallengeControllerTest extends TestCase { $expected = new \OCP\AppFramework\Http\TemplateResponse('core', 'twofactorshowchallenge', [ 'error' => true, 'provider' => $provider, + 'logout_attribute' => 'logoutAttribute', 'template' => '<html/>', ], 'guest'); diff --git a/tests/lib/AllConfigTest.php b/tests/lib/AllConfigTest.php index 4f8b0658b80..3d0a9cb0827 100644 --- a/tests/lib/AllConfigTest.php +++ b/tests/lib/AllConfigTest.php @@ -123,6 +123,25 @@ class AllConfigTest extends \Test\TestCase { $config->deleteUserValue('userPreCond', 'appPreCond', 'keyPreCond'); } + public function dataSetUserValueUnexpectedValue() { + return [ + [true], + [false], + [null], + [new \stdClass()], + ]; + } + + /** + * @dataProvider dataSetUserValueUnexpectedValue + * @param mixed $value + * @expectedException \UnexpectedValueException + */ + public function testSetUserValueUnexpectedValue($value) { + $config = $this->getConfig(); + $config->setUserValue('userSetBool', 'appSetBool', 'keySetBool', $value); + } + /** * @expectedException \OCP\PreConditionNotMetException */ diff --git a/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php index 8e53c9202cf..a398dc2320c 100644 --- a/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php @@ -16,7 +16,6 @@ use OC\AppFramework\Http\Request; use OC\AppFramework\Middleware\Security\CORSMiddleware; use OC\AppFramework\Utility\ControllerMethodReflector; use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; -use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; @@ -29,7 +28,9 @@ class CORSMiddlewareTest extends \Test\TestCase { protected function setUp() { parent::setUp(); $this->reflector = new ControllerMethodReflector(); - $this->session = $this->getMock('\OCP\IUserSession'); + $this->session = $this->getMockBuilder('\OC\User\Session') + ->disableOriginalConstructor() + ->getMock(); } /** @@ -127,7 +128,7 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->session->expects($this->never()) ->method('logout'); $this->session->expects($this->never()) - ->method('login') + ->method('logClientIn') ->with($this->equalTo('user'), $this->equalTo('pass')) ->will($this->returnValue(true)); $this->reflector->reflect($this, __FUNCTION__); @@ -150,7 +151,7 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->session->expects($this->once()) ->method('logout'); $this->session->expects($this->once()) - ->method('login') + ->method('logClientIn') ->with($this->equalTo('user'), $this->equalTo('pass')) ->will($this->returnValue(true)); $this->reflector->reflect($this, __FUNCTION__); @@ -175,7 +176,7 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->session->expects($this->once()) ->method('logout'); $this->session->expects($this->once()) - ->method('login') + ->method('logClientIn') ->with($this->equalTo('user'), $this->equalTo('pass')) ->will($this->returnValue(false)); $this->reflector->reflect($this, __FUNCTION__); diff --git a/tests/lib/Encryption/DecryptAllTest.php b/tests/lib/Encryption/DecryptAllTest.php index ffcbbc74a99..d7cf2fb7baf 100644 --- a/tests/lib/Encryption/DecryptAllTest.php +++ b/tests/lib/Encryption/DecryptAllTest.php @@ -86,13 +86,25 @@ class DecryptAllTest extends TestCase { $this->invokePrivate($this->instance, 'output', [$this->outputInterface]); } + public function dataDecryptAll() { + return [ + [true, 'user1', true], + [false, 'user1', true], + [true, '0', true], + [false, '0', true], + [true, '', false], + ]; + } + /** - * @dataProvider dataTrueFalse + * @dataProvider dataDecryptAll * @param bool $prepareResult + * @param string $user + * @param bool $userExistsChecked */ - public function testDecryptAll($prepareResult, $user) { + public function testDecryptAll($prepareResult, $user, $userExistsChecked) { - if (!empty($user)) { + if ($userExistsChecked) { $this->userManager->expects($this->once())->method('userExists')->willReturn(true); } else { $this->userManager->expects($this->never())->method('userExists'); @@ -125,15 +137,6 @@ class DecryptAllTest extends TestCase { $instance->decryptAll($this->inputInterface, $this->outputInterface, $user); } - public function dataTrueFalse() { - return [ - [true, 'user1'], - [false, 'user1'], - [true, ''], - [true, null] - ]; - } - /** * test decrypt all call with a user who doesn't exists */ @@ -147,8 +150,16 @@ class DecryptAllTest extends TestCase { ); } + public function dataTrueFalse() { + return [ + [true], + [false], + ]; + } + /** * @dataProvider dataTrueFalse + * @param bool $success */ public function testPrepareEncryptionModules($success) { diff --git a/tests/lib/Files/Storage/LocalTest.php b/tests/lib/Files/Storage/LocalTest.php index 7b8ae6a24b2..cca4d6a6676 100644 --- a/tests/lib/Files/Storage/LocalTest.php +++ b/tests/lib/Files/Storage/LocalTest.php @@ -84,5 +84,36 @@ class LocalTest extends Storage { public function testInvalidArgumentsNoArray() { new \OC\Files\Storage\Local(null); } + + /** + * @expectedException \OCP\Files\ForbiddenException + */ + public function testDisallowSymlinksOutsideDatadir() { + $subDir1 = $this->tmpDir . 'sub1'; + $subDir2 = $this->tmpDir . 'sub2'; + $sym = $this->tmpDir . 'sub1/sym'; + mkdir($subDir1); + mkdir($subDir2); + + symlink($subDir2, $sym); + + $storage = new \OC\Files\Storage\Local(['datadir' => $subDir1]); + + $storage->file_put_contents('sym/foo', 'bar'); + } + + public function testDisallowSymlinksInsideDatadir() { + $subDir1 = $this->tmpDir . 'sub1'; + $subDir2 = $this->tmpDir . 'sub1/sub2'; + $sym = $this->tmpDir . 'sub1/sym'; + mkdir($subDir1); + mkdir($subDir2); + + symlink($subDir2, $sym); + + $storage = new \OC\Files\Storage\Local(['datadir' => $subDir1]); + + $storage->file_put_contents('sym/foo', 'bar'); + } } diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 2c27bb64a70..59b17b83958 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -2417,7 +2417,7 @@ class ViewTest extends \Test\TestCase { $content = $view->getDirectoryContent('', $filter); - $files = array_map(function(FileInfo $info) { + $files = array_map(function (FileInfo $info) { return $info->getName(); }, $content); sort($files); @@ -2444,4 +2444,53 @@ class ViewTest extends \Test\TestCase { $data = $view->getFileInfo('.'); $this->assertEquals('', $data->getChecksum()); } + + public function testDeleteGhostFile() { + $storage = new Temporary(array()); + $scanner = $storage->getScanner(); + $cache = $storage->getCache(); + $storage->file_put_contents('foo.txt', 'bar'); + \OC\Files\Filesystem::mount($storage, array(), '/test/'); + $scanner->scan(''); + + $storage->unlink('foo.txt'); + + $this->assertTrue($cache->inCache('foo.txt')); + + $view = new \OC\Files\View('/test'); + $rootInfo = $view->getFileInfo(''); + $this->assertEquals(3, $rootInfo->getSize()); + $view->unlink('foo.txt'); + $newInfo = $view->getFileInfo(''); + + $this->assertFalse($cache->inCache('foo.txt')); + $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag()); + $this->assertEquals(0, $newInfo->getSize()); + } + + public function testDeleteGhostFolder() { + $storage = new Temporary(array()); + $scanner = $storage->getScanner(); + $cache = $storage->getCache(); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/foo.txt', 'bar'); + \OC\Files\Filesystem::mount($storage, array(), '/test/'); + $scanner->scan(''); + + $storage->rmdir('foo'); + + $this->assertTrue($cache->inCache('foo')); + $this->assertTrue($cache->inCache('foo/foo.txt')); + + $view = new \OC\Files\View('/test'); + $rootInfo = $view->getFileInfo(''); + $this->assertEquals(3, $rootInfo->getSize()); + $view->rmdir('foo'); + $newInfo = $view->getFileInfo(''); + + $this->assertFalse($cache->inCache('foo')); + $this->assertFalse($cache->inCache('foo/foo.txt')); + $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag()); + $this->assertEquals(0, $newInfo->getSize()); + } } diff --git a/tests/lib/LoggerTest.php b/tests/lib/LoggerTest.php index 4eb04b00f58..4b80c01f343 100644 --- a/tests/lib/LoggerTest.php +++ b/tests/lib/LoggerTest.php @@ -89,7 +89,7 @@ class LoggerTest extends TestCase { foreach($logLines as $logLine) { $this->assertNotContains($user, $logLine); $this->assertNotContains($password, $logLine); - $this->assertContains('login(*** username and password replaced ***)', $logLine); + $this->assertContains('login(*** sensitive parameters replaced ***)', $logLine); } } @@ -104,7 +104,7 @@ class LoggerTest extends TestCase { foreach($logLines as $logLine) { $this->assertNotContains($user, $logLine); $this->assertNotContains($password, $logLine); - $this->assertContains('checkPassword(*** username and password replaced ***)', $logLine); + $this->assertContains('checkPassword(*** sensitive parameters replaced ***)', $logLine); } } @@ -119,7 +119,7 @@ class LoggerTest extends TestCase { foreach($logLines as $logLine) { $this->assertNotContains($user, $logLine); $this->assertNotContains($password, $logLine); - $this->assertContains('validateUserPass(*** username and password replaced ***)', $logLine); + $this->assertContains('validateUserPass(*** sensitive parameters replaced ***)', $logLine); } } } diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index 36f14e85492..eac38ebba16 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -22,7 +22,7 @@ class SessionTest extends \Test\TestCase { private $timeFactory; /** @var \OC\Authentication\Token\DefaultTokenProvider */ - protected $defaultProvider; + protected $tokenProvider; /** @var \OCP\IConfig */ private $config; @@ -34,9 +34,7 @@ class SessionTest extends \Test\TestCase { $this->timeFactory->expects($this->any()) ->method('getTime') ->will($this->returnValue(10000)); - $this->defaultProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider') - ->disableOriginalConstructor() - ->getMock(); + $this->tokenProvider = $this->getMock('\OC\Authentication\Token\IProvider'); $this->config = $this->getMock('\OCP\IConfig'); } @@ -61,14 +59,14 @@ class SessionTest extends \Test\TestCase { $session->expects($this->once()) ->method('getId') ->will($this->returnValue($sessionId)); - $this->defaultProvider->expects($this->once()) + $this->tokenProvider->expects($this->once()) ->method('getToken') ->will($this->returnValue($token)); $session->expects($this->at(2)) ->method('get') ->with('last_login_check') ->will($this->returnValue(null)); // No check has been run yet - $this->defaultProvider->expects($this->once()) + $this->tokenProvider->expects($this->once()) ->method('getPassword') ->with($token, $sessionId) ->will($this->returnValue('password123')); @@ -87,7 +85,7 @@ class SessionTest extends \Test\TestCase { ->method('get') ->with('last_token_update') ->will($this->returnValue(null)); // No check run so far - $this->defaultProvider->expects($this->once()) + $this->tokenProvider->expects($this->once()) ->method('updateToken') ->with($token); $session->expects($this->at(5)) @@ -99,7 +97,7 @@ class SessionTest extends \Test\TestCase { ->with($expectedUser->getUID()) ->will($this->returnValue($expectedUser)); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); $user = $userSession->getUser(); $this->assertSame($expectedUser, $user); } @@ -122,7 +120,7 @@ class SessionTest extends \Test\TestCase { ->getMock(); $userSession = $this->getMockBuilder('\OC\User\Session') - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->defaultProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) ->setMethods([ 'getUser' ]) @@ -149,7 +147,7 @@ class SessionTest extends \Test\TestCase { ->method('getUID') ->will($this->returnValue('foo')); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); $userSession->setUser($user); } @@ -201,7 +199,7 @@ class SessionTest extends \Test\TestCase { ->will($this->returnValue($user)); $userSession = $this->getMockBuilder('\OC\User\Session') - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->defaultProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) ->setMethods([ 'prepareUserLogin' ]) @@ -248,7 +246,7 @@ class SessionTest extends \Test\TestCase { ->with('foo', 'bar') ->will($this->returnValue($user)); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); $userSession->login('foo', 'bar'); } @@ -284,7 +282,7 @@ class SessionTest extends \Test\TestCase { ->with('foo', 'bar') ->will($this->returnValue(false)); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); $userSession->login('foo', 'bar'); } @@ -304,7 +302,7 @@ class SessionTest extends \Test\TestCase { ->with('foo', 'bar') ->will($this->returnValue(false)); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); $userSession->login('foo', 'bar'); } @@ -316,11 +314,11 @@ class SessionTest extends \Test\TestCase { /** @var \OC\User\Session $userSession */ $userSession = $this->getMockBuilder('\OC\User\Session') - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->defaultProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) ->setMethods(['login']) ->getMock(); - $this->defaultProvider->expects($this->once()) + $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('doe') ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); @@ -341,11 +339,11 @@ class SessionTest extends \Test\TestCase { /** @var \OC\User\Session $userSession */ $userSession = $this->getMockBuilder('\OC\User\Session') - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->defaultProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) ->setMethods(['login', 'isTwoFactorEnforced']) ->getMock(); - $this->defaultProvider->expects($this->once()) + $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('doe') ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); @@ -413,7 +411,7 @@ class SessionTest extends \Test\TestCase { //override, otherwise tests will fail because of setcookie() array('setMagicInCookie'), //there are passed as parameters to the constructor - array($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config)); + array($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config)); $granted = $userSession->loginWithCookie('foo', $token); @@ -458,7 +456,7 @@ class SessionTest extends \Test\TestCase { $token = 'goodToken'; \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); $granted = $userSession->loginWithCookie('foo', 'badToken'); $this->assertSame($granted, false); @@ -501,7 +499,7 @@ class SessionTest extends \Test\TestCase { $token = 'goodToken'; \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); $granted = $userSession->loginWithCookie('foo', $token); $this->assertSame($granted, false); @@ -526,7 +524,7 @@ class SessionTest extends \Test\TestCase { $session = new Memory(''); $session->set('user_id', 'foo'); $userSession = $this->getMockBuilder('\OC\User\Session') - ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->defaultProvider, $this->config]) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) ->setMethods([ 'validateSession' ]) @@ -542,6 +540,119 @@ class SessionTest extends \Test\TestCase { $this->assertEquals($users['bar'], $userSession->getUser()); } + public function testCreateSessionToken() { + $manager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $session = $this->getMock('\OCP\ISession'); + $token = $this->getMock('\OC\Authentication\Token\IToken'); + $user = $this->getMock('\OCP\IUser'); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + + $random = $this->getMock('\OCP\Security\ISecureRandom'); + $config = $this->getMock('\OCP\IConfig'); + $csrf = $this->getMockBuilder('\OC\Security\CSRF\CsrfTokenManager') + ->disableOriginalConstructor() + ->getMock(); + $request = new \OC\AppFramework\Http\Request([ + 'server' => [ + 'HTTP_USER_AGENT' => 'Firefox', + ] + ], $random, $config, $csrf); + + $uid = 'user123'; + $loginName = 'User123'; + $password = 'passme'; + $sessionId = 'abcxyz'; + + $manager->expects($this->once()) + ->method('get') + ->with($uid) + ->will($this->returnValue($user)); + $session->expects($this->once()) + ->method('getId') + ->will($this->returnValue($sessionId)); + $this->tokenProvider->expects($this->once()) + ->method('getToken') + ->with($password) + ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); + + $this->tokenProvider->expects($this->once()) + ->method('generateToken') + ->with($sessionId, $uid, $loginName, $password, 'Firefox'); + + $this->assertTrue($userSession->createSessionToken($request, $uid, $loginName, $password)); + } + + public function testCreateSessionTokenWithTokenPassword() { + $manager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $session = $this->getMock('\OCP\ISession'); + $token = $this->getMock('\OC\Authentication\Token\IToken'); + $user = $this->getMock('\OCP\IUser'); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + + $random = $this->getMock('\OCP\Security\ISecureRandom'); + $config = $this->getMock('\OCP\IConfig'); + $csrf = $this->getMockBuilder('\OC\Security\CSRF\CsrfTokenManager') + ->disableOriginalConstructor() + ->getMock(); + $request = new \OC\AppFramework\Http\Request([ + 'server' => [ + 'HTTP_USER_AGENT' => 'Firefox', + ] + ], $random, $config, $csrf); + + $uid = 'user123'; + $loginName = 'User123'; + $password = 'iamatoken'; + $realPassword = 'passme'; + $sessionId = 'abcxyz'; + + $manager->expects($this->once()) + ->method('get') + ->with($uid) + ->will($this->returnValue($user)); + $session->expects($this->once()) + ->method('getId') + ->will($this->returnValue($sessionId)); + $this->tokenProvider->expects($this->once()) + ->method('getToken') + ->with($password) + ->will($this->returnValue($token)); + $this->tokenProvider->expects($this->once()) + ->method('getPassword') + ->with($token, $password) + ->will($this->returnValue($realPassword)); + + $this->tokenProvider->expects($this->once()) + ->method('generateToken') + ->with($sessionId, $uid, $loginName, $realPassword, 'Firefox'); + + $this->assertTrue($userSession->createSessionToken($request, $uid, $loginName, $password)); + } + + public function testCreateSessionTokenWithNonExistentUser() { + $manager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $session = $this->getMock('\OCP\ISession'); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); + $request = $this->getMock('\OCP\IRequest'); + + $uid = 'user123'; + $loginName = 'User123'; + $password = 'passme'; + + $manager->expects($this->once()) + ->method('get') + ->with($uid) + ->will($this->returnValue(null)); + + $this->assertFalse($userSession->createSessionToken($request, $uid, $loginName, $password)); + } + public function testTryTokenLoginWithDisabledUser() { $manager = $this->getMockBuilder('\OC\User\Manager') ->disableOriginalConstructor() @@ -549,14 +660,14 @@ class SessionTest extends \Test\TestCase { $session = new Memory(''); $token = $this->getMock('\OC\Authentication\Token\IToken'); $user = $this->getMock('\OCP\IUser'); - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, $this->config); + $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); $request = $this->getMock('\OCP\IRequest'); $request->expects($this->once()) ->method('getHeader') ->with('Authorization') ->will($this->returnValue('token xxxxx')); - $this->defaultProvider->expects($this->once()) + $this->tokenProvider->expects($this->once()) ->method('validateToken') ->with('xxxxx') ->will($this->returnValue($token)); diff --git a/version.php b/version.php index 967f2690276..698636a2196 100644 --- a/version.php +++ b/version.php @@ -25,10 +25,10 @@ // We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version = array(9, 1, 0, 7); +$OC_Version = array(9, 1, 0, 8); // The human readable string -$OC_VersionString = '9.1.0 beta 1'; +$OC_VersionString = '9.1.0 beta 2'; $OC_VersionCanBeUpgradedFrom = array(9, 0); |