Manager.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Authentication\Token;
  8. use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
  9. use OC\Authentication\Exceptions\InvalidTokenException as OcInvalidTokenException;
  10. use OC\Authentication\Exceptions\PasswordlessTokenException;
  11. use OCP\Authentication\Exceptions\ExpiredTokenException;
  12. use OCP\Authentication\Exceptions\InvalidTokenException;
  13. use OCP\Authentication\Exceptions\WipeTokenException;
  14. use OCP\Authentication\Token\IProvider as OCPIProvider;
  15. use OCP\Authentication\Token\IToken as OCPIToken;
  16. class Manager implements IProvider, OCPIProvider {
  17. /** @var PublicKeyTokenProvider */
  18. private $publicKeyTokenProvider;
  19. public function __construct(PublicKeyTokenProvider $publicKeyTokenProvider) {
  20. $this->publicKeyTokenProvider = $publicKeyTokenProvider;
  21. }
  22. /**
  23. * Create and persist a new token
  24. *
  25. * @param string $token
  26. * @param string $uid
  27. * @param string $loginName
  28. * @param string|null $password
  29. * @param string $name Name will be trimmed to 120 chars when longer
  30. * @param int $type token type
  31. * @param int $remember whether the session token should be used for remember-me
  32. * @return OCPIToken
  33. */
  34. public function generateToken(string $token,
  35. string $uid,
  36. string $loginName,
  37. $password,
  38. string $name,
  39. int $type = OCPIToken::TEMPORARY_TOKEN,
  40. int $remember = OCPIToken::DO_NOT_REMEMBER,
  41. ?array $scope = null,
  42. ): OCPIToken {
  43. if (mb_strlen($name) > 128) {
  44. $name = mb_substr($name, 0, 120) . '…';
  45. }
  46. try {
  47. return $this->publicKeyTokenProvider->generateToken(
  48. $token,
  49. $uid,
  50. $loginName,
  51. $password,
  52. $name,
  53. $type,
  54. $remember,
  55. $scope,
  56. );
  57. } catch (UniqueConstraintViolationException $e) {
  58. // It's rare, but if two requests of the same session (e.g. env-based SAML)
  59. // try to create the session token they might end up here at the same time
  60. // because we use the session ID as token and the db token is created anew
  61. // with every request.
  62. //
  63. // If the UIDs match, then this should be fine.
  64. $existing = $this->getToken($token);
  65. if ($existing->getUID() !== $uid) {
  66. throw new \Exception('Token conflict handled, but UIDs do not match. This should not happen', 0, $e);
  67. }
  68. return $existing;
  69. }
  70. }
  71. /**
  72. * Save the updated token
  73. *
  74. * @param OCPIToken $token
  75. * @throws InvalidTokenException
  76. */
  77. public function updateToken(OCPIToken $token) {
  78. $provider = $this->getProvider($token);
  79. $provider->updateToken($token);
  80. }
  81. /**
  82. * Update token activity timestamp
  83. *
  84. * @throws InvalidTokenException
  85. * @param OCPIToken $token
  86. */
  87. public function updateTokenActivity(OCPIToken $token) {
  88. $provider = $this->getProvider($token);
  89. $provider->updateTokenActivity($token);
  90. }
  91. /**
  92. * @param string $uid
  93. * @return OCPIToken[]
  94. */
  95. public function getTokenByUser(string $uid): array {
  96. return $this->publicKeyTokenProvider->getTokenByUser($uid);
  97. }
  98. /**
  99. * Get a token by token
  100. *
  101. * @param string $tokenId
  102. * @throws InvalidTokenException
  103. * @throws \RuntimeException when OpenSSL reports a problem
  104. * @return OCPIToken
  105. */
  106. public function getToken(string $tokenId): OCPIToken {
  107. try {
  108. return $this->publicKeyTokenProvider->getToken($tokenId);
  109. } catch (WipeTokenException $e) {
  110. throw $e;
  111. } catch (ExpiredTokenException $e) {
  112. throw $e;
  113. } catch (InvalidTokenException $e) {
  114. throw $e;
  115. }
  116. }
  117. /**
  118. * Get a token by token id
  119. *
  120. * @param int $tokenId
  121. * @throws InvalidTokenException
  122. * @return OCPIToken
  123. */
  124. public function getTokenById(int $tokenId): OCPIToken {
  125. try {
  126. return $this->publicKeyTokenProvider->getTokenById($tokenId);
  127. } catch (ExpiredTokenException $e) {
  128. throw $e;
  129. } catch (WipeTokenException $e) {
  130. throw $e;
  131. } catch (InvalidTokenException $e) {
  132. throw $e;
  133. }
  134. }
  135. /**
  136. * @param string $oldSessionId
  137. * @param string $sessionId
  138. * @throws InvalidTokenException
  139. * @return OCPIToken
  140. */
  141. public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken {
  142. try {
  143. return $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId);
  144. } catch (ExpiredTokenException $e) {
  145. throw $e;
  146. } catch (InvalidTokenException $e) {
  147. throw $e;
  148. }
  149. }
  150. /**
  151. * @param OCPIToken $savedToken
  152. * @param string $tokenId session token
  153. * @throws InvalidTokenException
  154. * @throws PasswordlessTokenException
  155. * @return string
  156. */
  157. public function getPassword(OCPIToken $savedToken, string $tokenId): string {
  158. $provider = $this->getProvider($savedToken);
  159. return $provider->getPassword($savedToken, $tokenId);
  160. }
  161. public function setPassword(OCPIToken $token, string $tokenId, string $password) {
  162. $provider = $this->getProvider($token);
  163. $provider->setPassword($token, $tokenId, $password);
  164. }
  165. public function invalidateToken(string $token) {
  166. $this->publicKeyTokenProvider->invalidateToken($token);
  167. }
  168. public function invalidateTokenById(string $uid, int $id) {
  169. $this->publicKeyTokenProvider->invalidateTokenById($uid, $id);
  170. }
  171. public function invalidateOldTokens() {
  172. $this->publicKeyTokenProvider->invalidateOldTokens();
  173. }
  174. public function invalidateLastUsedBefore(string $uid, int $before): void {
  175. $this->publicKeyTokenProvider->invalidateLastUsedBefore($uid, $before);
  176. }
  177. /**
  178. * @param OCPIToken $token
  179. * @param string $oldTokenId
  180. * @param string $newTokenId
  181. * @return OCPIToken
  182. * @throws InvalidTokenException
  183. * @throws \RuntimeException when OpenSSL reports a problem
  184. */
  185. public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken {
  186. if ($token instanceof PublicKeyToken) {
  187. return $this->publicKeyTokenProvider->rotate($token, $oldTokenId, $newTokenId);
  188. }
  189. /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */
  190. throw new OcInvalidTokenException();
  191. }
  192. /**
  193. * @param OCPIToken $token
  194. * @return IProvider
  195. * @throws InvalidTokenException
  196. */
  197. private function getProvider(OCPIToken $token): IProvider {
  198. if ($token instanceof PublicKeyToken) {
  199. return $this->publicKeyTokenProvider;
  200. }
  201. /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */
  202. throw new OcInvalidTokenException();
  203. }
  204. public function markPasswordInvalid(OCPIToken $token, string $tokenId) {
  205. $this->getProvider($token)->markPasswordInvalid($token, $tokenId);
  206. }
  207. public function updatePasswords(string $uid, string $password) {
  208. $this->publicKeyTokenProvider->updatePasswords($uid, $password);
  209. }
  210. public function invalidateTokensOfUser(string $uid, ?string $clientName) {
  211. $tokens = $this->getTokenByUser($uid);
  212. foreach ($tokens as $token) {
  213. if ($clientName === null || ($token->getName() === $clientName)) {
  214. $this->invalidateTokenById($uid, $token->getId());
  215. }
  216. }
  217. }
  218. }