Manager.php 7.6 KB

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