RetryJob.php 5.6 KB

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