This for example will allow rotating the apptoken for oauth Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>tags/v14.0.0beta1
@@ -30,9 +30,7 @@ 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 void setToken(string $token) | |||
* @method string getToken() | |||
* @method void setType(int $type) | |||
* @method int getType() | |||
@@ -173,4 +171,12 @@ class DefaultToken extends Entity implements IToken { | |||
public function getRemember(): int { | |||
return parent::getRemember(); | |||
} | |||
public function setToken(string $token) { | |||
parent::setToken($token); | |||
} | |||
public function setPassword(string $password = null) { | |||
parent::setPassword($password); | |||
} | |||
} |
@@ -273,6 +273,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, string $oldTokenId, string $newTokenId): IToken { | |||
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 |
@@ -145,4 +145,14 @@ interface IProvider { | |||
* @throws InvalidTokenException | |||
*/ | |||
public function setPassword(IToken $token, string $tokenId, string $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, string $oldTokenId, string $newTokenId): IToken; | |||
} |
@@ -96,7 +96,30 @@ interface IToken extends JsonSerializable { | |||
*/ | |||
public function setScope($scope); | |||
/** | |||
* Get the name of the token | |||
* @return string | |||
*/ | |||
public function getName(): string; | |||
/** | |||
* Get the remember state of the token | |||
* | |||
* @return int | |||
*/ | |||
public function getRemember(): int; | |||
/** | |||
* Set the token | |||
* | |||
* @param string $token | |||
*/ | |||
public function setToken(string $token); | |||
/** | |||
* Set the password | |||
* | |||
* @param string $password | |||
*/ | |||
public function setPassword(string $password); | |||
} |
@@ -416,4 +416,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'); | |||
} | |||
} |