diff options
Diffstat (limited to 'lib/private/Authentication/Token/Manager.php')
-rw-r--r-- | lib/private/Authentication/Token/Manager.php | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/lib/private/Authentication/Token/Manager.php b/lib/private/Authentication/Token/Manager.php new file mode 100644 index 00000000000..6953f47b004 --- /dev/null +++ b/lib/private/Authentication/Token/Manager.php @@ -0,0 +1,243 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Authentication\Token; + +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use OC\Authentication\Exceptions\InvalidTokenException as OcInvalidTokenException; +use OC\Authentication\Exceptions\PasswordlessTokenException; +use OCP\Authentication\Exceptions\ExpiredTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Token\IProvider as OCPIProvider; +use OCP\Authentication\Token\IToken as OCPIToken; + +class Manager implements IProvider, OCPIProvider { + /** @var PublicKeyTokenProvider */ + private $publicKeyTokenProvider; + + public function __construct(PublicKeyTokenProvider $publicKeyTokenProvider) { + $this->publicKeyTokenProvider = $publicKeyTokenProvider; + } + + /** + * Create and persist a new token + * + * @param string $token + * @param string $uid + * @param string $loginName + * @param string|null $password + * @param string $name Name will be trimmed to 120 chars when longer + * @param int $type token type + * @param int $remember whether the session token should be used for remember-me + * @return OCPIToken + */ + public function generateToken(string $token, + string $uid, + string $loginName, + $password, + string $name, + int $type = OCPIToken::TEMPORARY_TOKEN, + int $remember = OCPIToken::DO_NOT_REMEMBER, + ?array $scope = null, + ): OCPIToken { + if (mb_strlen($name) > 128) { + $name = mb_substr($name, 0, 120) . '…'; + } + + try { + return $this->publicKeyTokenProvider->generateToken( + $token, + $uid, + $loginName, + $password, + $name, + $type, + $remember, + $scope, + ); + } catch (UniqueConstraintViolationException $e) { + // It's rare, but if two requests of the same session (e.g. env-based SAML) + // try to create the session token they might end up here at the same time + // because we use the session ID as token and the db token is created anew + // with every request. + // + // If the UIDs match, then this should be fine. + $existing = $this->getToken($token); + if ($existing->getUID() !== $uid) { + throw new \Exception('Token conflict handled, but UIDs do not match. This should not happen', 0, $e); + } + return $existing; + } + } + + /** + * Save the updated token + * + * @param OCPIToken $token + * @throws InvalidTokenException + */ + public function updateToken(OCPIToken $token) { + $provider = $this->getProvider($token); + $provider->updateToken($token); + } + + /** + * Update token activity timestamp + * + * @throws InvalidTokenException + * @param OCPIToken $token + */ + public function updateTokenActivity(OCPIToken $token) { + $provider = $this->getProvider($token); + $provider->updateTokenActivity($token); + } + + /** + * @param string $uid + * @return OCPIToken[] + */ + public function getTokenByUser(string $uid): array { + return $this->publicKeyTokenProvider->getTokenByUser($uid); + } + + /** + * Get a token by token + * + * @param string $tokenId + * @throws InvalidTokenException + * @throws \RuntimeException when OpenSSL reports a problem + * @return OCPIToken + */ + public function getToken(string $tokenId): OCPIToken { + try { + return $this->publicKeyTokenProvider->getToken($tokenId); + } catch (WipeTokenException $e) { + throw $e; + } catch (ExpiredTokenException $e) { + throw $e; + } catch (InvalidTokenException $e) { + throw $e; + } + } + + /** + * Get a token by token id + * + * @param int $tokenId + * @throws InvalidTokenException + * @return OCPIToken + */ + public function getTokenById(int $tokenId): OCPIToken { + try { + return $this->publicKeyTokenProvider->getTokenById($tokenId); + } catch (ExpiredTokenException $e) { + throw $e; + } catch (WipeTokenException $e) { + throw $e; + } catch (InvalidTokenException $e) { + throw $e; + } + } + + /** + * @param string $oldSessionId + * @param string $sessionId + * @throws InvalidTokenException + * @return OCPIToken + */ + public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken { + try { + return $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId); + } catch (ExpiredTokenException $e) { + throw $e; + } catch (InvalidTokenException $e) { + throw $e; + } + } + + /** + * @param OCPIToken $savedToken + * @param string $tokenId session token + * @throws InvalidTokenException + * @throws PasswordlessTokenException + * @return string + */ + public function getPassword(OCPIToken $savedToken, string $tokenId): string { + $provider = $this->getProvider($savedToken); + return $provider->getPassword($savedToken, $tokenId); + } + + public function setPassword(OCPIToken $token, string $tokenId, string $password) { + $provider = $this->getProvider($token); + $provider->setPassword($token, $tokenId, $password); + } + + public function invalidateToken(string $token) { + $this->publicKeyTokenProvider->invalidateToken($token); + } + + public function invalidateTokenById(string $uid, int $id) { + $this->publicKeyTokenProvider->invalidateTokenById($uid, $id); + } + + public function invalidateOldTokens() { + $this->publicKeyTokenProvider->invalidateOldTokens(); + } + + public function invalidateLastUsedBefore(string $uid, int $before): void { + $this->publicKeyTokenProvider->invalidateLastUsedBefore($uid, $before); + } + + /** + * @param OCPIToken $token + * @param string $oldTokenId + * @param string $newTokenId + * @return OCPIToken + * @throws InvalidTokenException + * @throws \RuntimeException when OpenSSL reports a problem + */ + public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken { + if ($token instanceof PublicKeyToken) { + return $this->publicKeyTokenProvider->rotate($token, $oldTokenId, $newTokenId); + } + + /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */ + throw new OcInvalidTokenException(); + } + + /** + * @param OCPIToken $token + * @return IProvider + * @throws InvalidTokenException + */ + private function getProvider(OCPIToken $token): IProvider { + if ($token instanceof PublicKeyToken) { + return $this->publicKeyTokenProvider; + } + /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */ + throw new OcInvalidTokenException(); + } + + + public function markPasswordInvalid(OCPIToken $token, string $tokenId) { + $this->getProvider($token)->markPasswordInvalid($token, $tokenId); + } + + public function updatePasswords(string $uid, string $password) { + $this->publicKeyTokenProvider->updatePasswords($uid, $password); + } + + public function invalidateTokensOfUser(string $uid, ?string $clientName) { + $tokens = $this->getTokenByUser($uid); + foreach ($tokens as $token) { + if ($clientName === null || ($token->getName() === $clientName)) { + $this->invalidateTokenById($uid, $token->getId()); + } + } + } +} |