RetryJob.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OCA\LookupServerConnector\BackgroundJobs;
  8. use OC\Security\IdentityProof\Signer;
  9. use OCP\Accounts\IAccountManager;
  10. use OCP\AppFramework\Utility\ITimeFactory;
  11. use OCP\BackgroundJob\IJobList;
  12. use OCP\BackgroundJob\Job;
  13. use OCP\Http\Client\IClientService;
  14. use OCP\IConfig;
  15. use OCP\IUser;
  16. use OCP\IUserManager;
  17. class RetryJob extends Job {
  18. private IClientService $clientService;
  19. private string $lookupServer;
  20. private IConfig $config;
  21. private IUserManager $userManager;
  22. private IAccountManager $accountManager;
  23. private Signer $signer;
  24. protected int $retries = 0;
  25. protected bool $retainJob = false;
  26. /**
  27. * @param ITimeFactory $time
  28. * @param IClientService $clientService
  29. * @param IConfig $config
  30. * @param IUserManager $userManager
  31. * @param IAccountManager $accountManager
  32. * @param Signer $signer
  33. */
  34. public function __construct(ITimeFactory $time,
  35. IClientService $clientService,
  36. IConfig $config,
  37. IUserManager $userManager,
  38. IAccountManager $accountManager,
  39. Signer $signer) {
  40. parent::__construct($time);
  41. $this->clientService = $clientService;
  42. $this->config = $config;
  43. $this->userManager = $userManager;
  44. $this->accountManager = $accountManager;
  45. $this->signer = $signer;
  46. $this->lookupServer = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
  47. if (!empty($this->lookupServer)) {
  48. $this->lookupServer = rtrim($this->lookupServer, '/');
  49. $this->lookupServer .= '/users';
  50. }
  51. }
  52. /**
  53. * Run the job, then remove it from the jobList
  54. */
  55. public function start(IJobList $jobList): void {
  56. if (!isset($this->argument['userId'])) {
  57. // Old background job without user id, just drop it.
  58. $jobList->remove($this, $this->argument);
  59. return;
  60. }
  61. $this->retries = (int)$this->config->getUserValue($this->argument['userId'], 'lookup_server_connector', 'update_retries', '0');
  62. if ($this->shouldRemoveBackgroundJob()) {
  63. $jobList->remove($this, $this->argument);
  64. return;
  65. }
  66. if ($this->shouldRun()) {
  67. parent::start($jobList);
  68. if (!$this->retainJob) {
  69. $jobList->remove($this, $this->argument);
  70. }
  71. }
  72. }
  73. /**
  74. * Check if we should kill the background job:
  75. *
  76. * - internet connection is disabled
  77. * - no valid lookup server URL given
  78. * - lookup server was disabled by the admin
  79. * - max retries are reached (set to 5)
  80. */
  81. protected function shouldRemoveBackgroundJob(): bool {
  82. return $this->config->getSystemValueBool('has_internet_connection', true) === false ||
  83. $this->config->getSystemValueString('lookup_server', 'https://lookup.nextcloud.com') === '' ||
  84. $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes' ||
  85. $this->retries >= 5;
  86. }
  87. protected function shouldRun(): bool {
  88. $delay = 100 * 6 ** $this->retries;
  89. return ($this->time->getTime() - $this->lastRun) > $delay;
  90. }
  91. protected function run($argument): void {
  92. $user = $this->userManager->get($this->argument['userId']);
  93. if (!$user instanceof IUser) {
  94. // User does not exist anymore
  95. return;
  96. }
  97. $data = $this->getUserAccountData($user);
  98. $signedData = $this->signer->sign('lookupserver', $data, $user);
  99. $client = $this->clientService->newClient();
  100. try {
  101. if (count($data) === 1) {
  102. $dataOnLookupServer = $this->config->getUserValue($user->getUID(), 'lookup_server_connector', 'dataSend', '0') === '1';
  103. if (!$dataOnLookupServer) {
  104. // We never send data to the lookupserver so no need to delete it
  105. return;
  106. }
  107. // There is data on the lookup server so we must delete it
  108. $client->delete($this->lookupServer,
  109. [
  110. 'body' => json_encode($signedData),
  111. 'timeout' => 10,
  112. 'connect_timeout' => 3,
  113. ]
  114. );
  115. $this->config->setUserValue($user->getUID(), 'lookup_server_connector', 'dataSend', '0');
  116. } else {
  117. $client->post($this->lookupServer,
  118. [
  119. 'body' => json_encode($signedData),
  120. 'timeout' => 10,
  121. 'connect_timeout' => 3,
  122. ]
  123. );
  124. $this->config->setUserValue($user->getUID(), 'lookup_server_connector', 'dataSend', '1');
  125. }
  126. // Reset retry counter
  127. $this->config->deleteUserValue(
  128. $user->getUID(),
  129. 'lookup_server_connector',
  130. 'update_retries'
  131. );
  132. } catch (\Exception $e) {
  133. // An error occurred, retry later
  134. $this->retainJob = true;
  135. $this->config->setUserValue(
  136. $user->getUID(),
  137. 'lookup_server_connector',
  138. 'update_retries',
  139. $this->retries + 1
  140. );
  141. }
  142. }
  143. protected function getUserAccountData(IUser $user): array {
  144. $account = $this->accountManager->getAccount($user);
  145. $publicData = [];
  146. foreach ($account->getProperties() as $property) {
  147. if ($property->getScope() === IAccountManager::SCOPE_PUBLISHED) {
  148. $publicData[$property->getName()] = $property->getValue();
  149. }
  150. }
  151. $data = ['federationId' => $user->getCloudId()];
  152. if (!empty($publicData)) {
  153. $data['name'] = $publicData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] ?? '';
  154. $data['email'] = $publicData[IAccountManager::PROPERTY_EMAIL]['value'] ?? '';
  155. $data['address'] = $publicData[IAccountManager::PROPERTY_ADDRESS]['value'] ?? '';
  156. $data['website'] = $publicData[IAccountManager::PROPERTY_WEBSITE]['value'] ?? '';
  157. $data['twitter'] = $publicData[IAccountManager::PROPERTY_TWITTER]['value'] ?? '';
  158. $data['phone'] = $publicData[IAccountManager::PROPERTY_PHONE]['value'] ?? '';
  159. $data['twitter_signature'] = $publicData[IAccountManager::PROPERTY_TWITTER]['signature'] ?? '';
  160. $data['website_signature'] = $publicData[IAccountManager::PROPERTY_WEBSITE]['signature'] ?? '';
  161. $data['verificationStatus'] = [
  162. IAccountManager::PROPERTY_WEBSITE => $publicData[IAccountManager::PROPERTY_WEBSITE]['verified'] ?? '',
  163. IAccountManager::PROPERTY_TWITTER => $publicData[IAccountManager::PROPERTY_TWITTER]['verified'] ?? '',
  164. ];
  165. }
  166. return $data;
  167. }
  168. }