diff options
-rw-r--r-- | apps/dav/lib/CardDAV/Converter.php | 2 | ||||
-rw-r--r-- | apps/dav/tests/unit/CardDAV/ConverterTest.php | 46 | ||||
-rw-r--r-- | apps/encryption/lib/Crypto/Encryption.php | 8 | ||||
-rw-r--r-- | apps/encryption/lib/Session.php | 10 | ||||
-rw-r--r-- | apps/encryption/tests/Crypto/EncryptionTest.php | 15 | ||||
-rw-r--r-- | apps/encryption/tests/SessionTest.php | 26 | ||||
-rw-r--r-- | apps/files_sharing/lib/Activity/Providers/Downloads.php | 12 | ||||
-rw-r--r-- | core/js/sharedialogview.js | 12 | ||||
-rw-r--r-- | lib/base.php | 10 | ||||
-rw-r--r-- | lib/private/AppFramework/Http/Request.php | 33 | ||||
-rw-r--r-- | tests/lib/AppFramework/Http/RequestTest.php | 70 |
11 files changed, 222 insertions, 22 deletions
diff --git a/apps/dav/lib/CardDAV/Converter.php b/apps/dav/lib/CardDAV/Converter.php index 065c5494ec3..ba0c4c2a2d5 100644 --- a/apps/dav/lib/CardDAV/Converter.php +++ b/apps/dav/lib/CardDAV/Converter.php @@ -71,7 +71,7 @@ class Converter { break; case AccountManager::PROPERTY_AVATAR: if ($image !== null) { - $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]); + $vCard->add('PHOTO', 'data:'.$image->mimeType().';base64,' . base64_encode($image->data())); } break; case AccountManager::PROPERTY_EMAIL: diff --git a/apps/dav/tests/unit/CardDAV/ConverterTest.php b/apps/dav/tests/unit/CardDAV/ConverterTest.php index 737bbd96aaa..528b3aa9ef4 100644 --- a/apps/dav/tests/unit/CardDAV/ConverterTest.php +++ b/apps/dav/tests/unit/CardDAV/ConverterTest.php @@ -134,11 +134,45 @@ class ConverterTest extends TestCase { public function providesNewUsers() { return [ - [null], - [null, null, 'foo@bar.net'], - [['cloud' => 'foo@cloud.net', 'email' => 'foo@bar.net'], null, 'foo@bar.net', 'foo@cloud.net'], - [['cloud' => 'foo@cloud.net', 'email' => 'foo@bar.net', 'fn' => 'Dr. Foo Bar'], "Dr. Foo Bar", "foo@bar.net", 'foo@cloud.net'], - [['cloud' => 'foo@cloud.net', 'fn' => 'Dr. Foo Bar'], "Dr. Foo Bar", null, "foo@cloud.net"], + [ + null + ], + [ + null, + null, + 'foo@bar.net' + ], + [ + [ + 'cloud' => 'foo@cloud.net', + 'email' => 'foo@bar.net', + 'photo' => 'data:image/jpeg;base64,MTIzNDU2Nzg5', + ], + null, + 'foo@bar.net', + 'foo@cloud.net' + ], + [ + [ + 'cloud' => 'foo@cloud.net', + 'email' => 'foo@bar.net', + 'fn' => 'Dr. Foo Bar', + 'photo' => 'data:image/jpeg;base64,MTIzNDU2Nzg5', + ], + "Dr. Foo Bar", + "foo@bar.net", + 'foo@cloud.net' + ], + [ + [ + 'cloud' => 'foo@cloud.net', + 'fn' => 'Dr. Foo Bar', + 'photo' => 'data:image/jpeg;base64,MTIzNDU2Nzg5', + ], + "Dr. Foo Bar", + null, + "foo@cloud.net" + ], ]; } @@ -171,7 +205,7 @@ class ConverterTest extends TestCase { */ protected function getUserMock($displayName, $eMailAddress, $cloudId) { $image0 = $this->getMockBuilder(IImage::class)->disableOriginalConstructor()->getMock(); - $image0->method('mimeType')->willReturn('JPEG'); + $image0->method('mimeType')->willReturn('image/jpeg'); $image0->method('data')->willReturn('123456789'); $user = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock(); $user->method('getUID')->willReturn('12345'); diff --git a/apps/encryption/lib/Crypto/Encryption.php b/apps/encryption/lib/Crypto/Encryption.php index c71e25b6442..fdcbd41a09c 100644 --- a/apps/encryption/lib/Crypto/Encryption.php +++ b/apps/encryption/lib/Crypto/Encryption.php @@ -177,6 +177,14 @@ class Encryption implements IEncryptionModule { $this->isWriteOperation = false; $this->writeCache = ''; + if($this->session->isReady() === false) { + // if the master key is enabled we can initialize encryption + // with a empty password and user name + if ($this->util->isMasterKeyEnabled()) { + $this->keyManager->init('', ''); + } + } + if ($this->session->decryptAllModeActivated()) { $encryptedFileKey = $this->keyManager->getEncryptedFileKey($this->path); $shareKey = $this->keyManager->getShareKey($this->path, $this->session->getDecryptAllUid()); diff --git a/apps/encryption/lib/Session.php b/apps/encryption/lib/Session.php index 92132d6080c..a61ee25fadb 100644 --- a/apps/encryption/lib/Session.php +++ b/apps/encryption/lib/Session.php @@ -68,6 +68,16 @@ class Session { } /** + * check if encryption was initialized successfully + * + * @return bool + */ + public function isReady() { + $status = $this->getStatus(); + return $status === self::INIT_SUCCESSFUL; + } + + /** * Gets user or public share private key from session * * @return string $privateKey The user's plaintext private key diff --git a/apps/encryption/tests/Crypto/EncryptionTest.php b/apps/encryption/tests/Crypto/EncryptionTest.php index 658f6275bb4..3525d2d4aec 100644 --- a/apps/encryption/tests/Crypto/EncryptionTest.php +++ b/apps/encryption/tests/Crypto/EncryptionTest.php @@ -280,6 +280,21 @@ class EncryptionTest extends TestCase { } /** + * test begin() if encryption is not initialized but the master key is enabled + * in this case we can initialize the encryption without a username/password + * and continue + */ + public function testBeginInitMasterKey() { + + $this->sessionMock->expects($this->once())->method('isReady')->willReturn(false); + $this->utilMock->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + $this->keyManagerMock->expects($this->once())->method('init')->with('', ''); + + $this->instance->begin('/user/files/welcome.txt', 'user', 'r', [], []); + } + + /** * @dataProvider dataTestUpdate * * @param string $fileKey diff --git a/apps/encryption/tests/SessionTest.php b/apps/encryption/tests/SessionTest.php index 099acddbca1..3000fedf2c3 100644 --- a/apps/encryption/tests/SessionTest.php +++ b/apps/encryption/tests/SessionTest.php @@ -134,6 +134,32 @@ class SessionTest extends TestCase { } /** + * @dataProvider dataTestIsReady + * + * @param int $status + * @param bool $expected + */ + public function testIsReady($status, $expected) { + /** @var Session | \PHPUnit_Framework_MockObject_MockObject $instance */ + $instance = $this->getMockBuilder(Session::class) + ->setConstructorArgs([$this->sessionMock]) + ->setMethods(['getStatus'])->getMock(); + + $instance->expects($this->once())->method('getStatus') + ->willReturn($status); + + $this->assertSame($expected, $instance->isReady()); + } + + public function dataTestIsReady() { + return [ + [Session::INIT_SUCCESSFUL, true], + [Session::INIT_EXECUTED, false], + [Session::NOT_INITIALIZED, false], + ]; + } + + /** * @param $key * @param $value */ diff --git a/apps/files_sharing/lib/Activity/Providers/Downloads.php b/apps/files_sharing/lib/Activity/Providers/Downloads.php index 84ef5895dde..39e2a846b30 100644 --- a/apps/files_sharing/lib/Activity/Providers/Downloads.php +++ b/apps/files_sharing/lib/Activity/Providers/Downloads.php @@ -87,7 +87,7 @@ class Downloads implements IProvider { public function parseShortVersion(IEvent $event) { $parsedParameters = $this->getParsedParameters($event); - if ($event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED || + if ($event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED || $event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED) { $event->setParsedSubject($this->l->t('Downloaded via public link')) ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/download.svg'))); @@ -114,7 +114,7 @@ class Downloads implements IProvider { public function parseLongVersion(IEvent $event) { $parsedParameters = $this->getParsedParameters($event); - if ($event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED || + if ($event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED || $event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED) { $event->setParsedSubject($this->l->t('%1$s downloaded via public link', [ $parsedParameters['file']['path'], @@ -150,6 +150,11 @@ class Downloads implements IProvider { $parameters = $event->getSubjectParameters(); switch ($subject) { + case self::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED: + case self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED: + return [ + 'file' => $this->generateFileParameter($event->getObjectId(), $parameters[0]), + ]; case self::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED: case self::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED: return [ @@ -161,7 +166,8 @@ class Downloads implements IProvider { ], ]; } - return []; + + throw new \InvalidArgumentException(); } /** diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 6377d16dd4c..f83a2675958 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -265,17 +265,11 @@ var text = item.label; if (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) { - text = t('core', '{sharee} (group)', { - sharee: text - }); + text = t('core', '{sharee} (group)', { sharee: text }, undefined, { escape: false }); } else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) { - text = t('core', '{sharee} (remote)', { - sharee: text - }); + text = t('core', '{sharee} (remote)', { sharee: text }, undefined, { escape: false }); } else if (item.value.shareType === OC.Share.SHARE_TYPE_EMAIL) { - text = t('core', '{sharee} (email)', { - sharee: text - }); + text = t('core', '{sharee} (email)', { sharee: text }, undefined, { escape: false }); } var insert = $("<div class='share-autocomplete-item'/>"); var avatar = $("<div class='avatardiv'></div>").appendTo(insert); diff --git a/lib/base.php b/lib/base.php index d6c6e17eff9..2f5517f4614 100644 --- a/lib/base.php +++ b/lib/base.php @@ -493,10 +493,18 @@ class OC { 'lax', 'strict', ]; + + // Append __Host to the cookie if it meets the requirements + $cookiePrefix = ''; + if($cookieParams['secure'] === true && $cookieParams['path'] === '/') { + $cookiePrefix = '__Host-'; + } + foreach($policies as $policy) { header( sprintf( - 'Set-Cookie: nc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s', + 'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s', + $cookiePrefix, $policy, $cookieParams['path'], $policy diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php index c7a3be163fe..62d7fc7ed30 100644 --- a/lib/private/AppFramework/Http/Request.php +++ b/lib/private/AppFramework/Http/Request.php @@ -498,6 +498,31 @@ class Request implements \ArrayAccess, \Countable, IRequest { } /** + * Wrapper around session_get_cookie_params + * + * @return array + */ + protected function getCookieParams() { + return session_get_cookie_params(); + } + + /** + * Appends the __Host- prefix to the cookie if applicable + * + * @param string $name + * @return string + */ + protected function getProtectedCookieName($name) { + $cookieParams = $this->getCookieParams(); + $prefix = ''; + if($cookieParams['secure'] === true && $cookieParams['path'] === '/') { + $prefix = '__Host-'; + } + + return $prefix.$name; + } + + /** * Checks if the strict cookie has been sent with the request if the request * is including any cookies. * @@ -508,7 +533,9 @@ class Request implements \ArrayAccess, \Countable, IRequest { if(!$this->cookieCheckRequired()) { return true; } - if($this->getCookie('nc_sameSiteCookiestrict') === 'true' + + $cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict'); + if($this->getCookie($cookieName) === 'true' && $this->passesLaxCookieCheck()) { return true; } @@ -526,7 +553,9 @@ class Request implements \ArrayAccess, \Countable, IRequest { if(!$this->cookieCheckRequired()) { return true; } - if($this->getCookie('nc_sameSiteCookielax') === 'true') { + + $cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax'); + if($this->getCookie($cookieName) === 'true') { return true; } return false; diff --git a/tests/lib/AppFramework/Http/RequestTest.php b/tests/lib/AppFramework/Http/RequestTest.php index 1ba20869439..b1515b0efb5 100644 --- a/tests/lib/AppFramework/Http/RequestTest.php +++ b/tests/lib/AppFramework/Http/RequestTest.php @@ -1500,6 +1500,76 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesCSRFCheck()); } + public function testPassesStrictCookieCheckWithAllCookiesAndStrict() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName', 'getCookieParams']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + session_name() => 'asdf', + '__Host-nc_sameSiteCookiestrict' => 'true', + '__Host-nc_sameSiteCookielax' => 'true', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + $request + ->expects($this->any()) + ->method('getCookieParams') + ->willReturn([ + 'secure' => true, + 'path' => '/', + ]); + + $this->assertTrue($request->passesStrictCookieCheck()); + } + + public function testFailsStrictCookieCheckWithAllCookiesAndMissingStrict() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName', 'getCookieParams']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + session_name() => 'asdf', + 'nc_sameSiteCookiestrict' => 'true', + 'nc_sameSiteCookielax' => 'true', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + $request + ->expects($this->any()) + ->method('getCookieParams') + ->willReturn([ + 'secure' => true, + 'path' => '/', + ]); + + $this->assertFalse($request->passesStrictCookieCheck()); + } + + public function testGetCookieParams() { + $request = $this->createMock(Request::class); + $actual = $this->invokePrivate($request, 'getCookieParams'); + $this->assertSame(session_get_cookie_params(), $actual); + } + public function testPassesStrictCookieCheckWithAllCookies() { /** @var Request $request */ $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') |