DbHandler.php 7.5 KB

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