Skip to content

Commit 20c9366

Browse files
Merge pull request #2237 from nextcloud/backport/2235/stable28
[stable28] fix(push): Check last activity and last check for age
2 parents 8efd487 + 74ca7ff commit 20c9366

File tree

2 files changed

+81
-17
lines changed

2 files changed

+81
-17
lines changed

lib/Push.php

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use OCP\AppFramework\Http;
3636
use OCP\AppFramework\Utility\ITimeFactory;
3737
use OCP\Authentication\Exceptions\InvalidTokenException;
38+
use OCP\Authentication\Token\IToken;
3839
use OCP\DB\QueryBuilder\IQueryBuilder;
3940
use OCP\Http\Client\IClientService;
4041
use OCP\ICache;
@@ -581,27 +582,61 @@ protected function sendNotificationsToProxies(): void {
581582

582583
protected function validateToken(int $tokenId, int $maxAge): bool {
583584
$age = $this->cache->get('t' . $tokenId);
584-
if ($age !== null) {
585-
return $age > $maxAge;
585+
586+
if ($age === null) {
587+
try {
588+
// Check if the token is still valid...
589+
$token = $this->tokenProvider->getTokenById($tokenId);
590+
$type = $this->callSafelyForToken($token, 'getType');
591+
if ($type === IToken::WIPE_TOKEN) {
592+
// Token does not exist any more, should drop the push device entry
593+
$this->printInfo('Device token is marked for remote wipe');
594+
$this->deletePushToken($tokenId);
595+
$this->cache->set('t' . $tokenId, 0, 600);
596+
return false;
597+
}
598+
599+
$age = $token->getLastCheck();
600+
$lastActivity = $this->callSafelyForToken($token, 'getLastActivity');
601+
if ($lastActivity) {
602+
$age = max($age, $lastActivity);
603+
}
604+
$this->cache->set('t' . $tokenId, $age, 600);
605+
} catch (InvalidTokenException) {
606+
// Token does not exist any more, should drop the push device entry
607+
$this->printInfo('InvalidTokenException is thrown');
608+
$this->deletePushToken($tokenId);
609+
$this->cache->set('t' . $tokenId, 0, 600);
610+
return false;
611+
}
586612
}
587613

588-
try {
589-
// Check if the token is still valid...
590-
$token = $this->tokenProvider->getTokenById($tokenId);
591-
$this->cache->set('t' . $tokenId, $token->getLastCheck(), 600);
592-
if ($token->getLastCheck() > $maxAge) {
593-
$this->printInfo('Device token is valid');
594-
} else {
595-
$this->printInfo('Device token "last checked" is older than 60 days: ' . $token->getLastCheck());
614+
if ($age > $maxAge) {
615+
$this->printInfo('Device token is valid');
616+
return true;
617+
}
618+
619+
$this->printInfo('Device token "last checked" is older than 60 days: ' . $age);
620+
return false;
621+
}
622+
623+
/**
624+
* The functions are not part of public API so we are a bit more careful
625+
* @param IToken $token
626+
* @param 'getLastActivity'|'getType' $method
627+
* @return int|null
628+
*/
629+
protected function callSafelyForToken(IToken $token, string $method): ?int {
630+
if (method_exists($token, $method) || method_exists($token, '__call')) {
631+
try {
632+
$result = $token->$method();
633+
if (is_int($result)) {
634+
return $result;
635+
}
636+
} catch (\BadFunctionCallException) {
596637
}
597-
return $token->getLastCheck() > $maxAge;
598-
} catch (InvalidTokenException $e) {
599-
// Token does not exist anymore, should drop the push device entry
600-
$this->printInfo('InvalidTokenException is thrown');
601-
$this->deletePushToken($tokenId);
602-
$this->cache->set('t' . $tokenId, 0, 600);
603-
return false;
604638
}
639+
return null;
605640
}
606641

607642
/**

tests/Unit/PushTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@
2626
use GuzzleHttp\Exception\ServerException;
2727
use OC\Authentication\Exceptions\InvalidTokenException;
2828
use OC\Authentication\Token\IProvider;
29+
use OC\Authentication\Token\PublicKeyToken;
2930
use OC\Security\IdentityProof\Key;
3031
use OC\Security\IdentityProof\Manager;
3132
use OCA\Notifications\Push;
3233
use OCP\AppFramework\Http;
3334
use OCP\AppFramework\Utility\ITimeFactory;
35+
use OCP\Authentication\Token\IToken as OCPIToken;
3436
use OCP\Http\Client\IClient;
3537
use OCP\Http\Client\IClientService;
3638
use OCP\Http\Client\IResponse;
@@ -847,4 +849,31 @@ public function testPushToDeviceTalkNotification(array $deviceTypes, $isTalkNoti
847849

848850
$push->pushToDevice(200718, $notification);
849851
}
852+
853+
public static function dataValidateToken(): array {
854+
return [
855+
[1239999999, 1230000000, OCPIToken::WIPE_TOKEN, false],
856+
[1230000000, 1239999999, OCPIToken::WIPE_TOKEN, false],
857+
[1230000000, 1239999999, OCPIToken::PERMANENT_TOKEN, true],
858+
[1239999999, 1230000000, OCPIToken::PERMANENT_TOKEN, true],
859+
[1230000000, 1230000000, OCPIToken::PERMANENT_TOKEN, false],
860+
];
861+
}
862+
863+
/**
864+
* @dataProvider dataValidateToken
865+
*/
866+
public function testValidateToken(int $lastCheck, int $lastActivity, int $type, bool $expected): void {
867+
$token = PublicKeyToken::fromParams([
868+
'lastCheck' => $lastCheck,
869+
'lastActivity' => $lastActivity,
870+
'type' => $type,
871+
]);
872+
873+
$this->tokenProvider->method('getTokenById')
874+
->willReturn($token);
875+
876+
$push = $this->getPush();
877+
$this->assertSame($expected, self::invokePrivate($push, 'validateToken', [42, 1234567890]));
878+
}
850879
}

0 commit comments

Comments
 (0)