Skip to content

Commit 78960aa

Browse files
provokateurinbackportbot[bot]
authored andcommitted
fix(core): Stop abusing the cache for avatar upload
Signed-off-by: provokateurin <kate@provokateurin.de>
1 parent e4e193a commit 78960aa

5 files changed

Lines changed: 12 additions & 214 deletions

File tree

apps/settings/src/components/PersonalInfo/AvatarSection.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,7 @@ export default {
182182
if (data.status === 'success') {
183183
this.handleAvatarUpdate(false)
184184
} else if (data.data === 'notsquare') {
185-
const tempAvatar = generateUrl('/avatar/tmp') + '?requesttoken=' + encodeURIComponent(OC.requestToken) + '#' + Math.floor(Math.random() * 1000)
186-
this.$refs.cropper.replace(tempAvatar)
185+
this.$refs.cropper.replace(data.image)
187186
this.showCropper = true
188187
} else {
189188
showError(data.data.message)

build/integration/features/avatar.feature

Lines changed: 5 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,9 @@ Feature: avatar
2121
And last avatar is a square of size 512
2222
And last avatar is not a single color
2323

24-
25-
26-
Scenario: get temporary non-square user avatar before cropping it
27-
Given Logging in using web as "user0"
28-
And logged in user posts temporary avatar from file "data/coloured-pattern-non-square.png"
29-
When logged in user gets temporary avatar
30-
Then The following headers should be set
31-
| Content-Type | image/png |
32-
# "last avatar" also includes the last temporary avatar
33-
And last avatar is not a square
34-
And last avatar is not a single color
35-
36-
Scenario: get non-square user avatar before cropping it
37-
Given Logging in using web as "user0"
38-
And logged in user posts temporary avatar from file "data/coloured-pattern-non-square.png"
39-
# Avatar needs to be cropped to finish setting it
40-
When user "user0" gets avatar for user "user0"
41-
Then The following headers should be set
42-
| Content-Type | image/png |
43-
| X-NC-IsCustomAvatar | 0 |
44-
And last avatar is a square of size 512
45-
And last avatar is not a single color
46-
4724
Scenario: set square user avatar from file
4825
Given Logging in using web as "user0"
49-
When logged in user posts temporary avatar from file "data/green-square-256.png"
26+
When logged in user posts avatar from file "data/green-square-256.png"
5027
And user "user0" gets avatar for user "user0"
5128
And The following headers should be set
5229
| Content-Type | image/png |
@@ -64,7 +41,7 @@ Feature: avatar
6441
Scenario: set square user avatar from internal path
6542
Given user "user0" uploads file "data/green-square-256.png" to "/internal-green-square-256.png"
6643
And Logging in using web as "user0"
67-
When logged in user posts temporary avatar from internal path "internal-green-square-256.png"
44+
When logged in user posts avatar from internal path "internal-green-square-256.png"
6845
And user "user0" gets avatar for user "user0" with size "64"
6946
And The following headers should be set
7047
| Content-Type | image/png |
@@ -78,82 +55,21 @@ Feature: avatar
7855
And last avatar is a square of size 64
7956
And last avatar is a single "#00FF00" color
8057

81-
Scenario: set non-square user avatar from file
82-
Given Logging in using web as "user0"
83-
When logged in user posts temporary avatar from file "data/coloured-pattern-non-square.png"
84-
And logged in user crops temporary avatar
85-
| x | 384 |
86-
| y | 256 |
87-
| w | 128 |
88-
| h | 128 |
89-
Then logged in user gets temporary avatar with 404
90-
And user "user0" gets avatar for user "user0"
91-
And The following headers should be set
92-
| Content-Type | image/png |
93-
| X-NC-IsCustomAvatar | 1 |
94-
And last avatar is a square of size 512
95-
And last avatar is a single "#FF0000" color
96-
And user "anonymous" gets avatar for user "user0"
97-
And The following headers should be set
98-
| Content-Type | image/png |
99-
| X-NC-IsCustomAvatar | 1 |
100-
And last avatar is a square of size 512
101-
And last avatar is a single "#FF0000" color
102-
103-
Scenario: set non-square user avatar from internal path
104-
Given user "user0" uploads file "data/coloured-pattern-non-square.png" to "/internal-coloured-pattern-non-square.png"
105-
And Logging in using web as "user0"
106-
When logged in user posts temporary avatar from internal path "internal-coloured-pattern-non-square.png"
107-
And logged in user crops temporary avatar
108-
| x | 704 |
109-
| y | 320 |
110-
| w | 64 |
111-
| h | 64 |
112-
Then logged in user gets temporary avatar with 404
113-
And user "user0" gets avatar for user "user0" with size "64"
114-
And The following headers should be set
115-
| Content-Type | image/png |
116-
| X-NC-IsCustomAvatar | 1 |
117-
And last avatar is a square of size 64
118-
And last avatar is a single "#00FF00" color
119-
And user "anonymous" gets avatar for user "user0" with size "64"
120-
And The following headers should be set
121-
| Content-Type | image/png |
122-
| X-NC-IsCustomAvatar | 1 |
123-
And last avatar is a square of size 64
124-
And last avatar is a single "#00FF00" color
125-
126-
Scenario: cropped user avatar needs to be squared
127-
Given Logging in using web as "user0"
128-
And logged in user posts temporary avatar from file "data/coloured-pattern-non-square.png"
129-
When logged in user crops temporary avatar with 400
130-
| x | 384 |
131-
| y | 256 |
132-
| w | 192 |
133-
| h | 128 |
134-
135-
136-
13758
Scenario: delete user avatar
13859
Given Logging in using web as "user0"
139-
And logged in user posts temporary avatar from file "data/coloured-pattern-non-square.png"
140-
And logged in user crops temporary avatar
141-
| x | 384 |
142-
| y | 256 |
143-
| w | 128 |
144-
| h | 128 |
60+
And logged in user posts avatar from file "data/green-square-256.png"
14561
And user "user0" gets avatar for user "user0"
14662
And The following headers should be set
14763
| Content-Type | image/png |
14864
| X-NC-IsCustomAvatar | 1 |
14965
And last avatar is a square of size 512
150-
And last avatar is a single "#FF0000" color
66+
And last avatar is a single "#00FF00" color
15167
And user "anonymous" gets avatar for user "user0"
15268
And The following headers should be set
15369
| Content-Type | image/png |
15470
| X-NC-IsCustomAvatar | 1 |
15571
And last avatar is a square of size 512
156-
And last avatar is a single "#FF0000" color
72+
And last avatar is a single "#00FF00" color
15773
When logged in user deletes the user avatar
15874
Then user "user0" gets avatar for user "user0"
15975
And The following headers should be set
@@ -168,40 +84,6 @@ Feature: avatar
16884
And last avatar is a square of size 512
16985
And last avatar is not a single color
17086

171-
172-
173-
Scenario: get user avatar with a larger size than the original one
174-
Given Logging in using web as "user0"
175-
And logged in user posts temporary avatar from file "data/coloured-pattern-non-square.png"
176-
And logged in user crops temporary avatar
177-
| x | 384 |
178-
| y | 256 |
179-
| w | 128 |
180-
| h | 128 |
181-
When user "user0" gets avatar for user "user0" with size "192"
182-
Then The following headers should be set
183-
| Content-Type | image/png |
184-
| X-NC-IsCustomAvatar | 1 |
185-
And last avatar is a square of size 512
186-
And last avatar is a single "#FF0000" color
187-
188-
Scenario: get user avatar with a smaller size than the original one
189-
Given Logging in using web as "user0"
190-
And logged in user posts temporary avatar from file "data/coloured-pattern-non-square.png"
191-
And logged in user crops temporary avatar
192-
| x | 384 |
193-
| y | 256 |
194-
| w | 128 |
195-
| h | 128 |
196-
When user "user0" gets avatar for user "user0" with size "96"
197-
Then The following headers should be set
198-
| Content-Type | image/png |
199-
| X-NC-IsCustomAvatar | 1 |
200-
And last avatar is a square of size 512
201-
And last avatar is a single "#FF0000" color
202-
203-
204-
20587
Scenario: get default guest avatar
20688
When user "user0" gets avatar for guest "guest0"
20789
Then The following headers should be set

build/integration/features/bootstrap/Avatar.php

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
44
* SPDX-License-Identifier: AGPL-3.0-or-later
55
*/
6-
use Behat\Gherkin\Node\TableNode;
76
use PHPUnit\Framework\Assert;
87

98
require __DIR__ . '/../../vendor/autoload.php';
@@ -67,30 +66,11 @@ public function userGetsAvatarForGuest(string $user, string $guestAvatar) {
6766
}
6867

6968
/**
70-
* @When logged in user gets temporary avatar
71-
*/
72-
public function loggedInUserGetsTemporaryAvatar() {
73-
$this->loggedInUserGetsTemporaryAvatarWith('200');
74-
}
75-
76-
/**
77-
* @When logged in user gets temporary avatar with :statusCode
78-
*
79-
* @param string $statusCode
80-
*/
81-
public function loggedInUserGetsTemporaryAvatarWith(string $statusCode) {
82-
$this->sendingAToWithRequesttoken('GET', '/index.php/avatar/tmp');
83-
$this->theHTTPStatusCodeShouldBe($statusCode);
84-
85-
$this->getLastAvatar();
86-
}
87-
88-
/**
89-
* @When logged in user posts temporary avatar from file :source
69+
* @When logged in user posts avatar from file :source
9070
*
9171
* @param string $source
9272
*/
93-
public function loggedInUserPostsTemporaryAvatarFromFile(string $source) {
73+
public function loggedInUserPostsAvatarFromFile(string $source) {
9474
$file = \GuzzleHttp\Psr7\Utils::streamFor(fopen($source, 'r'));
9575

9676
$this->sendingAToWithRequesttoken('POST', '/index.php/avatar',
@@ -106,40 +86,15 @@ public function loggedInUserPostsTemporaryAvatarFromFile(string $source) {
10686
}
10787

10888
/**
109-
* @When logged in user posts temporary avatar from internal path :path
89+
* @When logged in user posts avatar from internal path :path
11090
*
11191
* @param string $path
11292
*/
113-
public function loggedInUserPostsTemporaryAvatarFromInternalPath(string $path) {
93+
public function loggedInUserPostsAvatarFromInternalPath(string $path) {
11494
$this->sendingAToWithRequesttoken('POST', '/index.php/avatar?path=' . $path);
11595
$this->theHTTPStatusCodeShouldBe('200');
11696
}
11797

118-
/**
119-
* @When logged in user crops temporary avatar
120-
*
121-
* @param TableNode $crop
122-
*/
123-
public function loggedInUserCropsTemporaryAvatar(TableNode $crop) {
124-
$this->loggedInUserCropsTemporaryAvatarWith('200', $crop);
125-
}
126-
127-
/**
128-
* @When logged in user crops temporary avatar with :statusCode
129-
*
130-
* @param string $statusCode
131-
* @param TableNode $crop
132-
*/
133-
public function loggedInUserCropsTemporaryAvatarWith(string $statusCode, TableNode $crop) {
134-
$parameters = [];
135-
foreach ($crop->getRowsHash() as $key => $value) {
136-
$parameters[] = 'crop[' . $key . ']=' . $value;
137-
}
138-
139-
$this->sendingAToWithRequesttoken('POST', '/index.php/avatar/cropped?' . implode('&', $parameters));
140-
$this->theHTTPStatusCodeShouldBe($statusCode);
141-
}
142-
14398
/**
14499
* @When logged in user deletes the user avatar
145100
*/

core/Controller/AvatarController.php

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@
1515
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
1616
use OCP\AppFramework\Http\Attribute\OpenAPI;
1717
use OCP\AppFramework\Http\Attribute\PublicPage;
18-
use OCP\AppFramework\Http\DataDisplayResponse;
1918
use OCP\AppFramework\Http\FileDisplayResponse;
2019
use OCP\AppFramework\Http\JSONResponse;
2120
use OCP\AppFramework\Http\Response;
2221
use OCP\Files\File;
2322
use OCP\Files\IRootFolder;
2423
use OCP\IAvatarManager;
25-
use OCP\ICache;
2624
use OCP\IL10N;
2725
use OCP\IRequest;
2826
use OCP\IUserManager;
@@ -38,7 +36,6 @@ public function __construct(
3836
string $appName,
3937
IRequest $request,
4038
protected IAvatarManager $avatarManager,
41-
protected ICache $cache,
4239
protected IL10N $l10n,
4340
protected IUserManager $userManager,
4441
protected IRootFolder $rootFolder,
@@ -199,8 +196,7 @@ public function postAvatar(?string $path = null): JSONResponse {
199196
Http::STATUS_BAD_REQUEST
200197
);
201198
}
202-
$this->cache->set('avatar_upload', file_get_contents($files['tmp_name'][0]), 7200);
203-
$content = $this->cache->get('avatar_upload');
199+
$content = file_get_contents($files['tmp_name'][0]);
204200
unlink($files['tmp_name'][0]);
205201
} else {
206202
$phpFileUploadErrors = [
@@ -247,18 +243,15 @@ public function postAvatar(?string $path = null): JSONResponse {
247243
try {
248244
$avatar = $this->avatarManager->getAvatar($this->userId);
249245
$avatar->set($image);
250-
// Clean up
251-
$this->cache->remove('tmpAvatar');
252246
return new JSONResponse(['status' => 'success']);
253247
} catch (\Throwable $e) {
254248
$this->logger->error($e->getMessage(), ['exception' => $e, 'app' => 'core']);
255249
return new JSONResponse(['data' => ['message' => $this->l10n->t('An error occurred. Please contact your admin.')]], Http::STATUS_BAD_REQUEST);
256250
}
257251
}
258252

259-
$this->cache->set('tmpAvatar', $image->data(), 7200);
260253
return new JSONResponse(
261-
['data' => 'notsquare'],
254+
['data' => 'notsquare', 'image' => 'data:' . $mimeType . ';base64,' . base64_encode($image->data())],
262255
Http::STATUS_OK
263256
);
264257
} else {

tests/Core/Controller/AvatarControllerTest.php

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ function is_uploaded_file($filename) {
2828
use OCP\Files\SimpleFS\ISimpleFile;
2929
use OCP\IAvatar;
3030
use OCP\IAvatarManager;
31-
use OCP\ICache;
3231
use OCP\IL10N;
3332
use OCP\IRequest;
3433
use OCP\IUser;
@@ -54,8 +53,6 @@ class AvatarControllerTest extends \Test\TestCase {
5453
private $avatarFile;
5554
/** @var IAvatarManager|\PHPUnit\Framework\MockObject\MockObject */
5655
private $avatarManager;
57-
/** @var ICache|\PHPUnit\Framework\MockObject\MockObject */
58-
private $cache;
5956
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
6057
private $l;
6158
/** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
@@ -73,8 +70,6 @@ protected function setUp(): void {
7370
parent::setUp();
7471

7572
$this->avatarManager = $this->getMockBuilder('OCP\IAvatarManager')->getMock();
76-
$this->cache = $this->getMockBuilder('OCP\ICache')
77-
->disableOriginalConstructor()->getMock();
7873
$this->l = $this->getMockBuilder(IL10N::class)->getMock();
7974
$this->l->method('t')->willReturnArgument(0);
8075
$this->userManager = $this->getMockBuilder(IUserManager::class)->getMock();
@@ -97,7 +92,6 @@ protected function setUp(): void {
9792
'core',
9893
$this->request,
9994
$this->avatarManager,
100-
$this->cache,
10195
$this->l,
10296
$this->userManager,
10397
$this->rootFolder,
@@ -297,25 +291,6 @@ public function testDeleteAvatarException(): void {
297291
$this->assertEquals($expectedResponse, $this->avatarController->deleteAvatar());
298292
}
299293

300-
/**
301-
* Trying to get a tmp avatar when it is not available. 404
302-
*/
303-
public function testTmpAvatarNoTmp(): void {
304-
$response = $this->avatarController->getTmpAvatar();
305-
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
306-
}
307-
308-
/**
309-
* Fetch tmp avatar
310-
*/
311-
public function testTmpAvatarValid(): void {
312-
$this->cache->method('get')->willReturn(file_get_contents(\OC::$SERVERROOT . '/tests/data/testimage.jpg'));
313-
314-
$response = $this->avatarController->getTmpAvatar();
315-
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
316-
}
317-
318-
319294
/**
320295
* When trying to post a new avatar a path or image should be posted.
321296
*/
@@ -334,9 +309,6 @@ public function testPostAvatarFile(): void {
334309
$copyRes = copy(\OC::$SERVERROOT . '/tests/data/testimage.jpg', $fileName);
335310
$this->assertTrue($copyRes);
336311

337-
//Create file in cache
338-
$this->cache->method('get')->willReturn(file_get_contents(\OC::$SERVERROOT . '/tests/data/testimage.jpg'));
339-
340312
//Create request return
341313
$reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [filesize(\OC::$SERVERROOT . '/tests/data/testimage.jpg')]];
342314
$this->request->method('getUploadedFile')->willReturn($reqRet);
@@ -372,9 +344,6 @@ public function testPostAvatarFileGif(): void {
372344
$copyRes = copy(\OC::$SERVERROOT . '/tests/data/testimage.gif', $fileName);
373345
$this->assertTrue($copyRes);
374346

375-
//Create file in cache
376-
$this->cache->method('get')->willReturn(file_get_contents(\OC::$SERVERROOT . '/tests/data/testimage.gif'));
377-
378347
//Create request return
379348
$reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [filesize(\OC::$SERVERROOT . '/tests/data/testimage.gif')]];
380349
$this->request->method('getUploadedFile')->willReturn($reqRet);

0 commit comments

Comments
 (0)