Skip to content

Commit b9df932

Browse files
committed
Merge pull request #15683 from owncloud/block-legacy-clients
Block old legacy clients
2 parents f63a0c9 + ab9ea97 commit b9df932

File tree

4 files changed

+220
-0
lines changed

4 files changed

+220
-0
lines changed

apps/files/appinfo/remote.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
// Load plugins
4545
$defaults = new OC_Defaults();
46+
$server->addPlugin(new \OC\Connector\Sabre\BlockLegacyClientPlugin(\OC::$server->getConfig()));
4647
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName()));
4748
// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to /
4849
$server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin());

config/config.sample.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,17 @@
799799
*/
800800
'cipher' => 'AES-256-CFB',
801801

802+
/**
803+
* The minimum ownCloud desktop client version that will be allowed to sync with
804+
* this server instance. All connections made from earlier clients will be denied
805+
* by the server. Defaults to the minimum officially supported ownCloud version at
806+
* the time of release of this server version.
807+
*
808+
* When changing this, note that older unsupported versions of the ownCloud desktop
809+
* client may not function as expected, and could lead to permanent data loss for
810+
* clients or other unexpected results.
811+
*/
812+
'minimum.supported.desktop.version' => '1.7.0',
802813

803814
/**
804815
* Memory caching backend configuration
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
/**
3+
* @author Lukas Reschke <[email protected]>
4+
*
5+
* @copyright Copyright (c) 2015, ownCloud, Inc.
6+
* @license AGPL-3.0
7+
*
8+
* This code is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License, version 3,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License, version 3,
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>
19+
*
20+
*/
21+
22+
namespace OC\Connector\Sabre;
23+
24+
use OCP\IConfig;
25+
use Sabre\HTTP\RequestInterface;
26+
use Sabre\DAV\ServerPlugin;
27+
use Sabre\DAV\Exception;
28+
29+
/**
30+
* Class BlockLegacyClientPlugin is used to detect old legacy sync clients and
31+
* returns a 403 status to those clients
32+
*
33+
* @package OC\Connector\Sabre
34+
*/
35+
class BlockLegacyClientPlugin extends ServerPlugin {
36+
/** @var \Sabre\DAV\Server */
37+
protected $server;
38+
/** @var IConfig */
39+
protected $config;
40+
41+
/**
42+
* @param IConfig $config
43+
*/
44+
public function __construct(IConfig $config) {
45+
$this->config = $config;
46+
}
47+
48+
/**
49+
* @param \Sabre\DAV\Server $server
50+
* @return void
51+
*/
52+
public function initialize(\Sabre\DAV\Server $server) {
53+
$this->server = $server;
54+
$this->server->on('beforeMethod', [$this, 'beforeHandler'], 200);
55+
}
56+
57+
/**
58+
* Detects all unsupported clients and throws a \Sabre\DAV\Exception\Forbidden
59+
* exception which will result in a 403 to them.
60+
* @param RequestInterface $request
61+
* @throws \Sabre\DAV\Exception\Forbidden If the client version is not supported
62+
*/
63+
public function beforeHandler(RequestInterface $request) {
64+
$userAgent = $request->getHeader('User-Agent');
65+
if($userAgent === null) {
66+
return;
67+
}
68+
69+
$minimumSupportedDesktopVersion = $this->config->getSystemValue('minimum.supported.desktop.version', '1.7.0');
70+
71+
// Match on the mirall version which is in scheme "Mozilla/5.0 (%1) mirall/%2" or
72+
// "mirall/%1" for older releases
73+
preg_match("/(?:mirall\\/)([\d.]+)/i", $userAgent, $versionMatches);
74+
if(isset($versionMatches[1]) &&
75+
version_compare($versionMatches[1], $minimumSupportedDesktopVersion) === -1) {
76+
throw new \Sabre\DAV\Exception\Forbidden('Unsupported client version.');
77+
}
78+
}
79+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
/**
3+
* @author Lukas Reschke <[email protected]>
4+
*
5+
* @copyright Copyright (c) 2015, ownCloud, Inc.
6+
* @license AGPL-3.0
7+
*
8+
* This code is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License, version 3,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License, version 3,
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>
19+
*
20+
*/
21+
22+
namespace Test\Connector\Sabre;
23+
24+
use OC\Connector\Sabre\BlockLegacyClientPlugin;
25+
use Test\TestCase;
26+
use OCP\IConfig;
27+
28+
/**
29+
* Class BlockLegacyClientPluginTest
30+
*
31+
* @package Test\Connector\Sabre
32+
*/
33+
class BlockLegacyClientPluginTest extends TestCase {
34+
/** @var IConfig */
35+
private $config;
36+
/** @var BlockLegacyClientPlugin */
37+
private $blockLegacyClientVersionPlugin;
38+
39+
public function setUp() {
40+
parent::setUp();
41+
42+
$this->config = $this->getMock('\OCP\IConfig');
43+
$this->blockLegacyClientVersionPlugin = new BlockLegacyClientPlugin($this->config);
44+
}
45+
46+
/**
47+
* @return array
48+
*/
49+
public function oldDesktopClientProvider() {
50+
return [
51+
['Mozilla/5.0 (1.5.0) mirall/1.5.0'],
52+
['mirall/1.5.0'],
53+
['mirall/1.5.4'],
54+
['mirall/1.6.0'],
55+
['Mozilla/5.0 (Bogus Text) mirall/1.6.9'],
56+
];
57+
}
58+
59+
/**
60+
* @dataProvider oldDesktopClientProvider
61+
* @param string $userAgent
62+
* @expectedException \Sabre\DAV\Exception\Forbidden
63+
* @expectedExceptionMessage Unsupported client version.
64+
*/
65+
public function testBeforeHandlerException($userAgent) {
66+
/** @var \Sabre\HTTP\RequestInterface $request */
67+
$request = $this->getMock('\Sabre\HTTP\RequestInterface');
68+
$request
69+
->expects($this->once())
70+
->method('getHeader')
71+
->with('User-Agent')
72+
->will($this->returnValue($userAgent));
73+
74+
$this->config
75+
->expects($this->once())
76+
->method('getSystemValue')
77+
->with('minimum.supported.desktop.version', '1.7.0')
78+
->will($this->returnValue('1.7.0'));
79+
80+
$this->blockLegacyClientVersionPlugin->beforeHandler($request);
81+
}
82+
83+
/**
84+
* @return array
85+
*/
86+
public function newAndAlternateDesktopClientProvider() {
87+
return [
88+
['Mozilla/5.0 (1.7.0) mirall/1.7.0'],
89+
['mirall/1.8.3'],
90+
['mirall/1.7.2'],
91+
['mirall/1.7.0'],
92+
['Mozilla/5.0 (Bogus Text) mirall/1.9.3'],
93+
];
94+
}
95+
96+
/**
97+
* @dataProvider newAndAlternateDesktopClientProvider
98+
* @param string $userAgent
99+
*/
100+
public function testBeforeHandlerSuccess($userAgent) {
101+
/** @var \Sabre\HTTP\RequestInterface $request */
102+
$request = $this->getMock('\Sabre\HTTP\RequestInterface');
103+
$request
104+
->expects($this->once())
105+
->method('getHeader')
106+
->with('User-Agent')
107+
->will($this->returnValue($userAgent));
108+
109+
$this->config
110+
->expects($this->once())
111+
->method('getSystemValue')
112+
->with('minimum.supported.desktop.version', '1.7.0')
113+
->will($this->returnValue('1.7.0'));
114+
115+
$this->blockLegacyClientVersionPlugin->beforeHandler($request);
116+
}
117+
118+
public function testBeforeHandlerNoUserAgent() {
119+
/** @var \Sabre\HTTP\RequestInterface $request */
120+
$request = $this->getMock('\Sabre\HTTP\RequestInterface');
121+
$request
122+
->expects($this->once())
123+
->method('getHeader')
124+
->with('User-Agent')
125+
->will($this->returnValue(null));
126+
$this->blockLegacyClientVersionPlugin->beforeHandler($request);
127+
}
128+
129+
}

0 commit comments

Comments
 (0)