logger = $logger; $this->httpClient = $httpClientService->newClient(); $this->jobList = $jobList; $this->urlGenerator = $urlGenerator; $this->ocsDiscoveryService = $ocsDiscoveryService; $this->trustedServers = $trustedServers; } /** * Run the job, then remove it from the joblist */ public function start(IJobList $jobList): void { $target = $this->argument['url']; // only execute if target is still in the list of trusted domains if ($this->trustedServers->isTrustedServer($target)) { $this->parentStart($jobList); } $jobList->remove($this, $this->argument); if ($this->retainJob) { $this->reAddJob($this->argument); } } protected function parentStart(IJobList $jobList): void { parent::start($jobList); } protected function run($argument) { $target = $argument['url']; $created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime(); $currentTime = $this->time->getTime(); $source = $this->urlGenerator->getAbsoluteURL('/'); $source = rtrim($source, '/'); $token = $argument['token']; // kill job after 30 days of trying $deadline = $currentTime - $this->maxLifespan; if ($created < $deadline) { $this->logger->warning("The job to get the shared secret job is too old and gets stopped now without retention. Setting server status of '{$target}' to failure."); $this->retainJob = false; $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE); return; } $endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING'); $endPoint = $endPoints['shared-secret'] ?? $this->defaultEndPoint; // make sure that we have a well formatted url $url = rtrim($target, '/') . '/' . trim($endPoint, '/'); $result = null; try { $result = $this->httpClient->get( $url, [ 'query' => [ 'url' => $source, 'token' => $token, 'format' => 'json', ], 'timeout' => 3, 'connect_timeout' => 3, ] ); $status = $result->getStatusCode(); } catch (ClientException $e) { $status = $e->getCode(); if ($status === Http::STATUS_FORBIDDEN) { $this->logger->info($target . ' refused to exchange a shared secret with you.', ['app' => 'federation']); } else { $this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage(), ['app' => 'federation']); } } catch (RequestException $e) { $status = -1; // There is no status code if we could not connect $this->logger->info('Could not connect to ' . $target, [ 'exception' => $e, ]); } catch (\Throwable $e) { $status = Http::STATUS_INTERNAL_SERVER_ERROR; $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); } // if we received a unexpected response we try again later if ( $status !== Http::STATUS_OK && $status !== Http::STATUS_FORBIDDEN ) { $this->retainJob = true; } if ($status === Http::STATUS_OK && $result instanceof IResponse) { $body = $result->getBody(); $result = json_decode($body, true); if (isset($result['ocs']['data']['sharedSecret'])) { $this->trustedServers->addSharedSecret( $target, $result['ocs']['data']['sharedSecret'] ); } else { $this->logger->error( 'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . $body, ['app' => 'federation'] ); $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE); } } } /** * Re-add background job * * @param array $argument */ protected function reAddJob(array $argument): void { $url = $argument['url']; $created = $argument['created'] ?? $this->time->getTime(); $token = $argument['token']; $this->jobList->add( GetSharedSecret::class, [ 'url' => $url, 'token' => $token, 'created' => $created ] ); } }