From 0885bd4ee543d8d9e4f3e561b44fcc90376dd45a Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Tue, 15 May 2018 21:10:43 +0200 Subject: [PATCH] Allow the rotation of tokens This for example will allow rotating the apptoken for oauth Signed-off-by: Roeland Jago Douma --- .../Authentication/Token/DefaultToken.php | 10 ++++- .../Token/DefaultTokenProvider.php | 22 ++++++++++ .../Authentication/Token/IProvider.php | 10 +++++ lib/private/Authentication/Token/IToken.php | 14 +++++++ .../Token/DefaultTokenProviderTest.php | 42 +++++++++++++++++++ 5 files changed, 96 insertions(+), 2 deletions(-) diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php index 127430ea6cb..024a6097fa8 100644 --- a/lib/private/Authentication/Token/DefaultToken.php +++ b/lib/private/Authentication/Token/DefaultToken.php @@ -28,10 +28,8 @@ use OCP\AppFramework\Db\Entity; * @method void setId(int $id) * @method void setUid(string $uid); * @method void setLoginName(string $loginName) - * @method void setPassword(string $password) * @method void setName(string $name) * @method string getName() - * @method void setToken(string $token) * @method string getToken() * @method void setType(string $type) * @method int getType() @@ -173,4 +171,12 @@ class DefaultToken extends Entity implements IToken { parent::setScope((string)$scope); } } + + public function setToken($token) { + parent::setToken($token); + } + + public function setPassword($password = null) { + parent::setPassword($password); + } } diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php index f099aca9d99..4f435e80e75 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -261,6 +261,28 @@ class DefaultTokenProvider implements IProvider { $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER); } + /** + * Rotate the token. Usefull for for example oauth tokens + * + * @param IToken $token + * @param string $oldTokenId + * @param string $newTokenId + * @return IToken + */ + public function rotate(IToken $token, $oldTokenId, $newTokenId) { + try { + $password = $this->getPassword($token, $oldTokenId); + $token->setPassword($this->encryptPassword($password, $newTokenId)); + } catch (PasswordlessTokenException $e) { + + } + + $token->setToken($this->hashToken($newTokenId)); + $this->updateToken($token); + + return $token; + } + /** * @param string $token * @return string diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php index 9f280263d76..5951cabad7f 100644 --- a/lib/private/Authentication/Token/IProvider.php +++ b/lib/private/Authentication/Token/IProvider.php @@ -135,4 +135,14 @@ interface IProvider { * @throws InvalidTokenException */ public function setPassword(IToken $token, $tokenId, $password); + + /** + * Rotate the token. Usefull for for example oauth tokens + * + * @param IToken $token + * @param string $oldTokenId + * @param string $newTokenId + * @return IToken + */ + public function rotate(IToken $token, $oldTokenId, $newTokenId); } diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php index 49745b266c4..16d97e65567 100644 --- a/lib/private/Authentication/Token/IToken.php +++ b/lib/private/Authentication/Token/IToken.php @@ -93,4 +93,18 @@ interface IToken extends JsonSerializable { * @param array $scope */ public function setScope($scope); + + /** + * Set the token + * + * @param string $token + */ + public function setToken($token); + + /** + * Set the password + * + * @param string $password + */ + public function setPassword($password); } diff --git a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php index 96fdbaa176f..eb69005655f 100644 --- a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php +++ b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php @@ -417,4 +417,46 @@ class DefaultTokenProviderTest extends TestCase { $this->tokenProvider->getTokenById(42); } + + public function testRotate() { + $token = new DefaultToken(); + $token->setPassword('oldencryptedpassword'); + + $this->config->method('getSystemValue') + ->with('secret') + ->willReturn('mysecret'); + + $this->crypto->method('decrypt') + ->with('oldencryptedpassword', 'oldtokenmysecret') + ->willReturn('mypassword'); + $this->crypto->method('encrypt') + ->with('mypassword', 'newtokenmysecret') + ->willReturn('newencryptedpassword'); + + $this->mapper->expects($this->once()) + ->method('update') + ->with($this->callback(function (DefaultToken $token) { + return $token->getPassword() === 'newencryptedpassword' && + $token->getToken() === hash('sha512', 'newtokenmysecret'); + })); + + $this->tokenProvider->rotate($token, 'oldtoken', 'newtoken'); + } + + public function testRotateNoPassword() { + $token = new DefaultToken(); + + $this->config->method('getSystemValue') + ->with('secret') + ->willReturn('mysecret'); + + $this->mapper->expects($this->once()) + ->method('update') + ->with($this->callback(function (DefaultToken $token) { + return $token->getPassword() === null && + $token->getToken() === hash('sha512', 'newtokenmysecret'); + })); + + $this->tokenProvider->rotate($token, 'oldtoken', 'newtoken'); + } }