Manager.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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): OCPIToken {
  41. if (mb_strlen($name) > 128) {
  42. $name = mb_substr($name, 0, 120) . '…';
  43. }
  44. try {
  45. return $this->publicKeyTokenProvider->generateToken(
  46. $token,
  47. $uid,
  48. $loginName,
  49. $password,
  50. $name,
  51. $type,
  52. $remember
  53. );
  54. } catch (UniqueConstraintViolationException $e) {
  55. // It's rare, but if two requests of the same session (e.g. env-based SAML)
  56. // try to create the session token they might end up here at the same time
  57. // because we use the session ID as token and the db token is created anew
  58. // with every request.
  59. //
  60. // If the UIDs match, then this should be fine.
  61. $existing = $this->getToken($token);
  62. if ($existing->getUID() !== $uid) {
  63. throw new \Exception('Token conflict handled, but UIDs do not match. This should not happen', 0, $e);
  64. }
  65. return $existing;
  66. }
  67. }
  68. /**
  69. * Save the updated token
  70. *
  71. * @param OCPIToken $token
  72. * @throws InvalidTokenException
  73. */
  74. public function updateToken(OCPIToken $token) {
  75. $provider = $this->getProvider($token);
  76. $provider->updateToken($token);
  77. }
  78. /**
  79. * Update token activity timestamp
  80. *
  81. * @throws InvalidTokenException
  82. * @param OCPIToken $token
  83. */
  84. public function updateTokenActivity(OCPIToken $token) {
  85. $provider = $this->getProvider($token);
  86. $provider->updateTokenActivity($token);
  87. }
  88. /**
  89. * @param string $uid
  90. * @return OCPIToken[]
  91. */
  92. public function getTokenByUser(string $uid): array {
  93. return $this->publicKeyTokenProvider->getTokenByUser($uid);
  94. }
  95. /**
  96. * Get a token by token
  97. *
  98. * @param string $tokenId
  99. * @throws InvalidTokenException
  100. * @throws \RuntimeException when OpenSSL reports a problem
  101. * @return OCPIToken
  102. */
  103. public function getToken(string $tokenId): OCPIToken {
  104. try {
  105. return $this->publicKeyTokenProvider->getToken($tokenId);
  106. } catch (WipeTokenException $e) {
  107. throw $e;
  108. } catch (ExpiredTokenException $e) {
  109. throw $e;
  110. } catch (InvalidTokenException $e) {
  111. throw $e;
  112. }
  113. }
  114. /**
  115. * Get a token by token id
  116. *
  117. * @param int $tokenId
  118. * @throws InvalidTokenException
  119. * @return OCPIToken
  120. */
  121. public function getTokenById(int $tokenId): OCPIToken {
  122. try {
  123. return $this->publicKeyTokenProvider->getTokenById($tokenId);
  124. } catch (ExpiredTokenException $e) {
  125. throw $e;
  126. } catch (WipeTokenException $e) {
  127. throw $e;
  128. } catch (InvalidTokenException $e) {
  129. throw $e;
  130. }
  131. }
  132. /**
  133. * @param string $oldSessionId
  134. * @param string $sessionId
  135. * @throws InvalidTokenException
  136. * @return OCPIToken
  137. */
  138. public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken {
  139. try {
  140. return $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId);
  141. } catch (ExpiredTokenException $e) {
  142. throw $e;
  143. } catch (InvalidTokenException $e) {
  144. throw $e;
  145. }
  146. }
  147. /**
  148. * @param OCPIToken $savedToken
  149. * @param string $tokenId session token
  150. * @throws InvalidTokenException
  151. * @throws PasswordlessTokenException
  152. * @return string
  153. */
  154. public function getPassword(OCPIToken $savedToken, string $tokenId): string {
  155. $provider = $this->getProvider($savedToken);
  156. return $provider->getPassword($savedToken, $tokenId);
  157. }
  158. public function setPassword(OCPIToken $token, string $tokenId, string $password) {
  159. $provider = $this->getProvider($token);
  160. $provider->setPassword($token, $tokenId, $password);
  161. }
  162. public function invalidateToken(string $token) {
  163. $this->publicKeyTokenProvider->invalidateToken($token);
  164. }
  165. public function invalidateTokenById(string $uid, int $id) {
  166. $this->publicKeyTokenProvider->invalidateTokenById($uid, $id);
  167. }
  168. public function invalidateOldTokens() {
  169. $this->publicKeyTokenProvider->invalidateOldTokens();
  170. }
  171. public function invalidateLastUsedBefore(string $uid, int $before): void {
  172. $this->publicKeyTokenProvider->invalidateLastUsedBefore($uid, $before);
  173. }
  174. /**
  175. * @param OCPIToken $token
  176. * @param string $oldTokenId
  177. * @param string $newTokenId
  178. * @return OCPIToken
  179. * @throws InvalidTokenException
  180. * @throws \RuntimeException when OpenSSL reports a problem
  181. */
  182. public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken {
  183. if ($token instanceof PublicKeyToken) {
  184. return $this->publicKeyTokenProvider->rotate($token, $oldTokenId, $newTokenId);
  185. }
  186. /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */
  187. throw new OcInvalidTokenException();
  188. }
  189. /**
  190. * @param OCPIToken $token
  191. * @return IProvider
  192. * @throws InvalidTokenException
  193. */
  194. private function getProvider(OCPIToken $token): IProvider {
  195. if ($token instanceof PublicKeyToken) {
  196. return $this->publicKeyTokenProvider;
  197. }
  198. /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */
  199. throw new OcInvalidTokenException();
  200. }
  201. public function markPasswordInvalid(OCPIToken $token, string $tokenId) {
  202. $this->getProvider($token)->markPasswordInvalid($token, $tokenId);
  203. }
  204. public function updatePasswords(string $uid, string $password) {
  205. $this->publicKeyTokenProvider->updatePasswords($uid, $password);
  206. }
  207. public function invalidateTokensOfUser(string $uid, ?string $clientName) {
  208. $tokens = $this->getTokenByUser($uid);
  209. foreach ($tokens as $token) {
  210. if ($clientName === null || ($token->getName() === $clientName)) {
  211. $this->invalidateTokenById($uid, $token->getId());
  212. }
  213. }
  214. }
  215. }