DbHandler.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors*
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\Federation;
  8. use OC\Files\Filesystem;
  9. use OCP\DB\Exception as DBException;
  10. use OCP\DB\QueryBuilder\IQueryBuilder;
  11. use OCP\HintException;
  12. use OCP\IDBConnection;
  13. use OCP\IL10N;
  14. /**
  15. * Class DbHandler
  16. *
  17. * Handles all database calls for the federation app
  18. *
  19. * @todo Port to QBMapper
  20. *
  21. * @group DB
  22. * @package OCA\Federation
  23. */
  24. class DbHandler {
  25. private string $dbTable = 'trusted_servers';
  26. public function __construct(
  27. private IDBConnection $connection,
  28. private IL10N $IL10N,
  29. ) {
  30. }
  31. /**
  32. * Add server to the list of trusted servers
  33. *
  34. * @throws HintException
  35. */
  36. public function addServer(string $url): int {
  37. $hash = $this->hash($url);
  38. $url = rtrim($url, '/');
  39. $query = $this->connection->getQueryBuilder();
  40. $query->insert($this->dbTable)
  41. ->values([
  42. 'url' => $query->createParameter('url'),
  43. 'url_hash' => $query->createParameter('url_hash'),
  44. ])
  45. ->setParameter('url', $url)
  46. ->setParameter('url_hash', $hash);
  47. $result = $query->executeStatement();
  48. if ($result) {
  49. return $query->getLastInsertId();
  50. }
  51. $message = 'Internal failure, Could not add trusted server: ' . $url;
  52. $message_t = $this->IL10N->t('Could not add server');
  53. throw new HintException($message, $message_t);
  54. return -1;
  55. }
  56. /**
  57. * Remove server from the list of trusted servers
  58. */
  59. public function removeServer(int $id): void {
  60. $query = $this->connection->getQueryBuilder();
  61. $query->delete($this->dbTable)
  62. ->where($query->expr()->eq('id', $query->createParameter('id')))
  63. ->setParameter('id', $id);
  64. $query->executeStatement();
  65. }
  66. /**
  67. * Get trusted server with given ID
  68. *
  69. * @return array{id: int, url: string, url_hash: string, token: ?string, shared_secret: ?string, status: int, sync_token: ?string}
  70. * @throws \Exception
  71. */
  72. public function getServerById(int $id): array {
  73. $query = $this->connection->getQueryBuilder();
  74. $query->select('*')->from($this->dbTable)
  75. ->where($query->expr()->eq('id', $query->createParameter('id')))
  76. ->setParameter('id', $id, IQueryBuilder::PARAM_INT);
  77. $qResult = $query->executeQuery();
  78. $result = $qResult->fetchAll();
  79. $qResult->closeCursor();
  80. if (empty($result)) {
  81. throw new \Exception('No Server found with ID: ' . $id);
  82. }
  83. return $result[0];
  84. }
  85. /**
  86. * Get all trusted servers
  87. *
  88. * @return list<array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}>
  89. * @throws DBException
  90. */
  91. public function getAllServer(): array {
  92. $query = $this->connection->getQueryBuilder();
  93. $query->select(['url', 'url_hash', 'id', 'status', 'shared_secret', 'sync_token'])
  94. ->from($this->dbTable);
  95. $statement = $query->executeQuery();
  96. $result = $statement->fetchAll();
  97. $statement->closeCursor();
  98. return $result;
  99. }
  100. /**
  101. * Check if server already exists in the database table
  102. */
  103. public function serverExists(string $url): bool {
  104. $hash = $this->hash($url);
  105. $query = $this->connection->getQueryBuilder();
  106. $query->select('url')
  107. ->from($this->dbTable)
  108. ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
  109. ->setParameter('url_hash', $hash);
  110. $statement = $query->executeQuery();
  111. $result = $statement->fetchAll();
  112. $statement->closeCursor();
  113. return !empty($result);
  114. }
  115. /**
  116. * Write token to database. Token is used to exchange the secret
  117. */
  118. public function addToken(string $url, string $token): void {
  119. $hash = $this->hash($url);
  120. $query = $this->connection->getQueryBuilder();
  121. $query->update($this->dbTable)
  122. ->set('token', $query->createParameter('token'))
  123. ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
  124. ->setParameter('url_hash', $hash)
  125. ->setParameter('token', $token);
  126. $query->executeStatement();
  127. }
  128. /**
  129. * Get token stored in database
  130. * @throws \Exception
  131. */
  132. public function getToken(string $url): string {
  133. $hash = $this->hash($url);
  134. $query = $this->connection->getQueryBuilder();
  135. $query->select('token')->from($this->dbTable)
  136. ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
  137. ->setParameter('url_hash', $hash);
  138. $statement = $query->executeQuery();
  139. $result = $statement->fetch();
  140. $statement->closeCursor();
  141. if (!isset($result['token'])) {
  142. throw new \Exception('No token found for: ' . $url);
  143. }
  144. return $result['token'];
  145. }
  146. /**
  147. * Add shared Secret to database
  148. */
  149. public function addSharedSecret(string $url, string $sharedSecret): void {
  150. $hash = $this->hash($url);
  151. $query = $this->connection->getQueryBuilder();
  152. $query->update($this->dbTable)
  153. ->set('shared_secret', $query->createParameter('sharedSecret'))
  154. ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
  155. ->setParameter('url_hash', $hash)
  156. ->setParameter('sharedSecret', $sharedSecret);
  157. $query->executeStatement();
  158. }
  159. /**
  160. * Get shared secret from database
  161. */
  162. public function getSharedSecret(string $url): string {
  163. $hash = $this->hash($url);
  164. $query = $this->connection->getQueryBuilder();
  165. $query->select('shared_secret')->from($this->dbTable)
  166. ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
  167. ->setParameter('url_hash', $hash);
  168. $statement = $query->executeQuery();
  169. $result = $statement->fetch();
  170. $statement->closeCursor();
  171. return (string)$result['shared_secret'];
  172. }
  173. /**
  174. * Set server status
  175. */
  176. public function setServerStatus(string $url, int $status, ?string $token = null): void {
  177. $hash = $this->hash($url);
  178. $query = $this->connection->getQueryBuilder();
  179. $query->update($this->dbTable)
  180. ->set('status', $query->createNamedParameter($status))
  181. ->where($query->expr()->eq('url_hash', $query->createNamedParameter($hash)));
  182. if (!is_null($token)) {
  183. $query->set('sync_token', $query->createNamedParameter($token));
  184. }
  185. $query->executeStatement();
  186. }
  187. /**
  188. * Get server status
  189. */
  190. public function getServerStatus(string $url): int {
  191. $hash = $this->hash($url);
  192. $query = $this->connection->getQueryBuilder();
  193. $query->select('status')->from($this->dbTable)
  194. ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
  195. ->setParameter('url_hash', $hash);
  196. $statement = $query->executeQuery();
  197. $result = $statement->fetch();
  198. $statement->closeCursor();
  199. return (int)$result['status'];
  200. }
  201. /**
  202. * Create hash from URL
  203. */
  204. protected function hash(string $url): string {
  205. $normalized = $this->normalizeUrl($url);
  206. return sha1($normalized);
  207. }
  208. /**
  209. * Normalize URL, used to create the sha1 hash
  210. */
  211. protected function normalizeUrl(string $url): string {
  212. $normalized = $url;
  213. if (strpos($url, 'https://') === 0) {
  214. $normalized = substr($url, strlen('https://'));
  215. } elseif (strpos($url, 'http://') === 0) {
  216. $normalized = substr($url, strlen('http://'));
  217. }
  218. $normalized = Filesystem::normalizePath($normalized);
  219. $normalized = trim($normalized, '/');
  220. return $normalized;
  221. }
  222. public function auth(string $username, string $password): bool {
  223. if ($username !== 'system') {
  224. return false;
  225. }
  226. $query = $this->connection->getQueryBuilder();
  227. $query->select('url')->from($this->dbTable)
  228. ->where($query->expr()->eq('shared_secret', $query->createNamedParameter($password)));
  229. $statement = $query->executeQuery();
  230. $result = $statement->fetch();
  231. $statement->closeCursor();
  232. return !empty($result);
  233. }
  234. }