Skip to content

Commit

Permalink
Merge pull request #157 from nextcloud/feature/25/block_older_clients
Browse files Browse the repository at this point in the history
Update minimum version for supported clients
  • Loading branch information
georgehrke authored Jul 16, 2020
2 parents 93618dc + 7d38ec5 commit 57dd204
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 79 deletions.
45 changes: 21 additions & 24 deletions lib/UserAgentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ class UserAgentManager {

public function __construct() {
$this->supportedUserAgents = [
'/^Mozilla\/5\.0 \(Android\) Nextcloud\-android.*$/' => '',
Request::USER_AGENT_CLIENT_DESKTOP => '',
'/^Mozilla\/5\.0 \(iOS\) Nextcloud\-iOS.*$/' => '2.20.0',
'/^Mozilla\/5\.0 \(Android\) Nextcloud\-android\/(?<version>(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)).*$/' => '3.13.0',
'/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/(?<version>(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)).*$/' => '2.7.0',
'/^Mozilla\/5\.0 \(iOS\) Nextcloud\-iOS\/(?<version>(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)).*$/' => '3.0.0',
];
}

Expand All @@ -51,34 +51,31 @@ public function __construct() {
* @return bool
*/
public function supportsEndToEndEncryption(string $client): bool {
foreach ($this->supportedUserAgents as $regex => $minVersion) {
if (preg_match($regex, $client)) {
return $this->checkVersion($client, $minVersion);
$supportedUAs = $this->getSupportedUserAgents();

foreach ($supportedUAs as $regex => $minVersion) {
$doesMatch = preg_match($regex, $client, $matches);
if ($doesMatch === 0) {
continue;
}

if (empty($minVersion)) {
return true;
}
if (!isset($matches['version'])) {
return false;
}

return (version_compare($matches['version'], $minVersion) > -1);
}

return false;
}

/**
* check the client version
*
* @param string $client
* @param string $minVersion
* @return bool returns true if clientVersion >= minVersion or if no min Version is specified
* @return array|string[]
*/
protected function checkVersion(string $client, string $minVersion): bool {

// no minVersion given, all client versions are compatible
if (empty($minVersion)) {
return true;
}

$version = substr(strrchr($client, '/'), 1);
if (!empty($version) && version_compare($version, $minVersion) > -1) {
return true;
}

return false;
protected function getSupportedUserAgents(): array {
return $this->supportedUserAgents;
}
}
121 changes: 66 additions & 55 deletions tests/Unit/UserAgentManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,84 +25,95 @@
namespace OCA\EndToEndEncryption\Tests\Unit;

use OCA\EndToEndEncryption\UserAgentManager;
use PHPUnit_Framework_MockObject_MockObject;
use Test\TestCase;

class UserAgentManagerTest extends TestCase {

/**
* create userAgentManager instance
* @param string $client
* @param bool $expected
*
* @param array $mockedMethods
* @return UserAgentManager|PHPUnit_Framework_MockObject_MockObject
* @dataProvider supportsEndToEndEncryptionDataProvider
*/
private function getUserAgentManager($mockedMethods = []) {
if (empty($mockedMethods)) {
return new UserAgentManager();
}
public function testSupportsEndToEndEncryption(string $client,
bool $expected): void {
$supportedUAs = $this->getSupportedUserAgents();
$userAgentManager = $this->getUserAgentManager(['getSupportedUserAgents']);
$userAgentManager->expects($this->once())
->method('getSupportedUserAgents')
->willReturn($supportedUAs);

$userAgentManager = $this->getMockBuilder(UserAgentManager::class)
->setMethods($mockedMethods)->getMock();
return $userAgentManager;
$actual = $userAgentManager->supportsEndToEndEncryption($client);
$this->assertEquals($expected, $actual);
}

/**
* @dataProvider dataTestCheckVersion
*
* @param string $client
* @param string $minVersion
* @param bool $expected
* @return array
*/
public function testCheckVersion($client, $minVersion, $expected): void {
$userAgentManager = $this->getUserAgentManager();
$result = $this->invokePrivate($userAgentManager, 'checkVersion', [$client, $minVersion]);
$this->assertSame($expected, $result);
}

public function dataTestCheckVersion(): array {
public function supportsEndToEndEncryptionDataProvider(): array {
return [
// Android
['Mozilla/5.0 (Android) Nextcloud-android/2.1.3', '', true],
['Mozilla/5.0 (Android) Nextcloud-android/2.1.3', '1.9.3', true],
['Mozilla/5.0 (Android) Nextcloud-android/2.1.3', '2.1.3', true],
['Mozilla/5.0 (Android) Nextcloud-android/2.1.3', '2.1.4', false],
['Mozilla/5.0 (Android) Nextcloud-android/1.9.9', false],
['Mozilla/5.0 (Android) Nextcloud-android/2.1.3', false],
['Mozilla/5.0 (Android) Nextcloud-android/2.3.3', false],
['Mozilla/5.0 (Android) Nextcloud-android/2.3.4', true],
['Mozilla/5.0 (Android) Nextcloud-android/2.4.9', true],
['Mozilla/5.0 (Android) Nextcloud-android/3.0.0', true],
// Android without version
['Mozilla/5.0 (Android) Nextcloud-android/beta', false],
['Mozilla/5.0 (Android) Nextcloud-android/', false],
['Mozilla/5.0 (Android) Nextcloud-android', false],
// iOS
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.1.3', '', true],
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.1.3', '1.9.3', true],
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.1.3', '2.1.3', true],
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.1.3', '2.1.4', false],
// no valid version should result in false
['Mozilla/5.0 (Android) Nextcloud-android/', '2.1.4', false],
['Mozilla/5.0 (Android) Nextcloud-android/zzz', '2.1.4', false],
['Mozilla/5.0 (iOS) Nextcloud-iOS/1.9.9', false],
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.1.3', false],
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.3.3', false],
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.3.4', true],
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.4.9', true],
['Mozilla/5.0 (iOS) Nextcloud-iOS/3.0.0', true],
// iOS without version
['Mozilla/5.0 (iOS) Nextcloud-iOS/beta', false],
['Mozilla/5.0 (iOS) Nextcloud-iOS/', false],
['Mozilla/5.0 (iOS) Nextcloud-iOS', false],
// Desktop
['Mozilla/5.0 (Macintosh) mirall/1.9.9stable (build 20200303) (Nextcloud)', false],
['Mozilla/5.0 (Macintosh) mirall/2.1.3rc (build 20200303)', false],
['Mozilla/5.0 (Macintosh) mirall/2.3.3', false],
['Mozilla/5.0 (Linux) mirall/2.3.4', true],
['Mozilla/5.0 (Macintosh) csyncoC/2.4.9RC (build 20200303) (Nextcloud)', true],
['Mozilla/5.0 (Macintosh) mirall/3.0.0 (build 20200303)', true],
// Desktop without version
['Mozilla/5.0 (Macintosh) mirall/ (build 20200303)', false],
['Mozilla/5.0 (Macintosh) mirall/', false],
['Mozilla/5.0 (Macintosh) mirall', false],
];
}

private function getUserAgentManager(array $mockedMethods = []) {
if (empty($mockedMethods)) {
return new UserAgentManager();
}

return $this
->getMockBuilder(UserAgentManager::class)
->setMethods($mockedMethods)
->getMock();
}

/**
* @dataProvider dataTestSupportsEndToEndEncryption
* This function returns the user agents to test against
* It keeps the original regex, but replaces the exact version
* so this test suite doesn't break on a simple version bump
*
* @param string $client
* @param bool $expected
* @return array
*/
public function testSupportsEndToEndEncryption($client, $expected): void {
$userAgentManager = $this->getUserAgentManager(['checkVersion']);
private function getSupportedUserAgents(): array {
$userAgentManager = new UserAgentManager();
$originalRules = self::invokePrivate($userAgentManager, 'getSupportedUserAgents');

if ($expected === true) {
$userAgentManager->expects($this->once())->method('checkVersion')
->willReturn($expected);
} else {
$userAgentManager->expects($this->never())->method('checkVersion');
foreach ($originalRules as $regex => $version) {
$originalRules[$regex] = '2.3.4';
}

$result = $userAgentManager->supportsEndToEndEncryption($client);
$this->assertSame($expected, $result);
}

public function dataTestSupportsEndToEndEncryption(): array {
return [
['Mozilla/5.0 (Android) ownCloud-android/2.1.3', false],
['Mozilla/5.0 (iOS) ownCloud-iOS/2.20.1', false],
['Mozilla/5.0 (iOS) Nextcloud-iOS/2.20.1', true],
['Mozilla/5.0 (Android) Nextcloud-android/2.1.3', true],
];
return $originalRules;
}
}

0 comments on commit 57dd204

Please sign in to comment.