Completed
Push — master ( 3fa81d...4ab573 )
by Christoph
26:17 queued 22s
created
apps/federation/lib/SyncJob.php 2 patches
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -12,24 +12,24 @@
 block discarded – undo
12 12
 use Psr\Log\LoggerInterface;
13 13
 
14 14
 class SyncJob extends TimedJob {
15
-	public function __construct(
16
-		protected SyncFederationAddressBooks $syncService,
17
-		protected LoggerInterface $logger,
18
-		ITimeFactory $timeFactory,
19
-	) {
20
-		parent::__construct($timeFactory);
21
-		// Run once a day
22
-		$this->setInterval(24 * 60 * 60);
23
-		$this->setTimeSensitivity(self::TIME_INSENSITIVE);
24
-	}
15
+    public function __construct(
16
+        protected SyncFederationAddressBooks $syncService,
17
+        protected LoggerInterface $logger,
18
+        ITimeFactory $timeFactory,
19
+    ) {
20
+        parent::__construct($timeFactory);
21
+        // Run once a day
22
+        $this->setInterval(24 * 60 * 60);
23
+        $this->setTimeSensitivity(self::TIME_INSENSITIVE);
24
+    }
25 25
 
26
-	protected function run($argument) {
27
-		$this->syncService->syncThemAll(function ($url, $ex): void {
28
-			if ($ex instanceof \Exception) {
29
-				$this->logger->error("Error while syncing $url.", [
30
-					'exception' => $ex,
31
-				]);
32
-			}
33
-		});
34
-	}
26
+    protected function run($argument) {
27
+        $this->syncService->syncThemAll(function ($url, $ex): void {
28
+            if ($ex instanceof \Exception) {
29
+                $this->logger->error("Error while syncing $url.", [
30
+                    'exception' => $ex,
31
+                ]);
32
+            }
33
+        });
34
+    }
35 35
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -24,7 +24,7 @@
 block discarded – undo
24 24
 	}
25 25
 
26 26
 	protected function run($argument) {
27
-		$this->syncService->syncThemAll(function ($url, $ex): void {
27
+		$this->syncService->syncThemAll(function($url, $ex): void {
28 28
 			if ($ex instanceof \Exception) {
29 29
 				$this->logger->error("Error while syncing $url.", [
30 30
 					'exception' => $ex,
Please login to merge, or discard this patch.
apps/federation/lib/BackgroundJob/RequestSharedSecret.php 2 patches
Indentation   +139 added lines, -139 removed lines patch added patch discarded remove patch
@@ -31,143 +31,143 @@
 block discarded – undo
31 31
  * @package OCA\Federation\Backgroundjob
32 32
  */
33 33
 class RequestSharedSecret extends Job {
34
-	private IClient $httpClient;
35
-
36
-	protected bool $retainJob = false;
37
-
38
-	private string $defaultEndPoint = '/ocs/v2.php/apps/federation/api/v1/request-shared-secret';
39
-
40
-	/** @var int 30 day = 2592000sec */
41
-	private int $maxLifespan = 2592000;
42
-
43
-	public function __construct(
44
-		IClientService $httpClientService,
45
-		private IURLGenerator $urlGenerator,
46
-		private IJobList $jobList,
47
-		private TrustedServers $trustedServers,
48
-		private IDiscoveryService $ocsDiscoveryService,
49
-		private LoggerInterface $logger,
50
-		ITimeFactory $timeFactory,
51
-		private IConfig $config,
52
-	) {
53
-		parent::__construct($timeFactory);
54
-		$this->httpClient = $httpClientService->newClient();
55
-	}
56
-
57
-
58
-	/**
59
-	 * run the job, then remove it from the joblist
60
-	 */
61
-	public function start(IJobList $jobList): void {
62
-		$target = $this->argument['url'];
63
-		// only execute if target is still in the list of trusted domains
64
-		if ($this->trustedServers->isTrustedServer($target)) {
65
-			$this->parentStart($jobList);
66
-		}
67
-
68
-		$jobList->remove($this, $this->argument);
69
-
70
-		if ($this->retainJob) {
71
-			$this->reAddJob($this->argument);
72
-		}
73
-	}
74
-
75
-	/**
76
-	 * Call start() method of parent
77
-	 * Useful for unit tests
78
-	 */
79
-	protected function parentStart(IJobList $jobList): void {
80
-		parent::start($jobList);
81
-	}
82
-
83
-	/**
84
-	 * @param array $argument
85
-	 * @return void
86
-	 */
87
-	protected function run($argument) {
88
-		$target = $argument['url'];
89
-		$created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
90
-		$currentTime = $this->time->getTime();
91
-		$source = $this->urlGenerator->getAbsoluteURL('/');
92
-		$source = rtrim($source, '/');
93
-		$token = $argument['token'];
94
-
95
-		// kill job after 30 days of trying
96
-		$deadline = $currentTime - $this->maxLifespan;
97
-		if ($created < $deadline) {
98
-			$this->logger->warning("The job to request the shared secret job is too old and gets stopped now without retention. Setting server status of '{$target}' to failure.");
99
-			$this->retainJob = false;
100
-			$this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
101
-			return;
102
-		}
103
-
104
-		$endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING');
105
-		$endPoint = $endPoints['shared-secret'] ?? $this->defaultEndPoint;
106
-
107
-		// make sure that we have a well formatted url
108
-		$url = rtrim($target, '/') . '/' . trim($endPoint, '/');
109
-
110
-		try {
111
-			$result = $this->httpClient->post(
112
-				$url,
113
-				[
114
-					'body' => [
115
-						'url' => $source,
116
-						'token' => $token,
117
-						'format' => 'json',
118
-					],
119
-					'timeout' => 3,
120
-					'connect_timeout' => 3,
121
-					'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
122
-				]
123
-			);
124
-
125
-			$status = $result->getStatusCode();
126
-		} catch (ClientException $e) {
127
-			$status = $e->getCode();
128
-			if ($status === Http::STATUS_FORBIDDEN) {
129
-				$this->logger->info($target . ' refused to ask for a shared secret.');
130
-			} else {
131
-				$this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage());
132
-			}
133
-		} catch (RequestException $e) {
134
-			$status = -1; // There is no status code if we could not connect
135
-			$this->logger->info('Could not connect to ' . $target);
136
-		} catch (\Throwable $e) {
137
-			$status = Http::STATUS_INTERNAL_SERVER_ERROR;
138
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
139
-		}
140
-
141
-		// if we received a unexpected response we try again later
142
-		if (
143
-			$status !== Http::STATUS_OK
144
-			&& ($status !== Http::STATUS_FORBIDDEN || $this->getAttempt($argument) < 5)
145
-		) {
146
-			$this->retainJob = true;
147
-		}
148
-	}
149
-
150
-	/**
151
-	 * re-add background job
152
-	 */
153
-	protected function reAddJob(array $argument): void {
154
-		$url = $argument['url'];
155
-		$created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
156
-		$token = $argument['token'];
157
-		$attempt = $this->getAttempt($argument) + 1;
158
-
159
-		$this->jobList->add(
160
-			RequestSharedSecret::class,
161
-			[
162
-				'url' => $url,
163
-				'token' => $token,
164
-				'created' => $created,
165
-				'attempt' => $attempt
166
-			]
167
-		);
168
-	}
169
-
170
-	protected function getAttempt(array $argument): int {
171
-		return $argument['attempt'] ?? 0;
172
-	}
34
+    private IClient $httpClient;
35
+
36
+    protected bool $retainJob = false;
37
+
38
+    private string $defaultEndPoint = '/ocs/v2.php/apps/federation/api/v1/request-shared-secret';
39
+
40
+    /** @var int 30 day = 2592000sec */
41
+    private int $maxLifespan = 2592000;
42
+
43
+    public function __construct(
44
+        IClientService $httpClientService,
45
+        private IURLGenerator $urlGenerator,
46
+        private IJobList $jobList,
47
+        private TrustedServers $trustedServers,
48
+        private IDiscoveryService $ocsDiscoveryService,
49
+        private LoggerInterface $logger,
50
+        ITimeFactory $timeFactory,
51
+        private IConfig $config,
52
+    ) {
53
+        parent::__construct($timeFactory);
54
+        $this->httpClient = $httpClientService->newClient();
55
+    }
56
+
57
+
58
+    /**
59
+     * run the job, then remove it from the joblist
60
+     */
61
+    public function start(IJobList $jobList): void {
62
+        $target = $this->argument['url'];
63
+        // only execute if target is still in the list of trusted domains
64
+        if ($this->trustedServers->isTrustedServer($target)) {
65
+            $this->parentStart($jobList);
66
+        }
67
+
68
+        $jobList->remove($this, $this->argument);
69
+
70
+        if ($this->retainJob) {
71
+            $this->reAddJob($this->argument);
72
+        }
73
+    }
74
+
75
+    /**
76
+     * Call start() method of parent
77
+     * Useful for unit tests
78
+     */
79
+    protected function parentStart(IJobList $jobList): void {
80
+        parent::start($jobList);
81
+    }
82
+
83
+    /**
84
+     * @param array $argument
85
+     * @return void
86
+     */
87
+    protected function run($argument) {
88
+        $target = $argument['url'];
89
+        $created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
90
+        $currentTime = $this->time->getTime();
91
+        $source = $this->urlGenerator->getAbsoluteURL('/');
92
+        $source = rtrim($source, '/');
93
+        $token = $argument['token'];
94
+
95
+        // kill job after 30 days of trying
96
+        $deadline = $currentTime - $this->maxLifespan;
97
+        if ($created < $deadline) {
98
+            $this->logger->warning("The job to request the shared secret job is too old and gets stopped now without retention. Setting server status of '{$target}' to failure.");
99
+            $this->retainJob = false;
100
+            $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
101
+            return;
102
+        }
103
+
104
+        $endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING');
105
+        $endPoint = $endPoints['shared-secret'] ?? $this->defaultEndPoint;
106
+
107
+        // make sure that we have a well formatted url
108
+        $url = rtrim($target, '/') . '/' . trim($endPoint, '/');
109
+
110
+        try {
111
+            $result = $this->httpClient->post(
112
+                $url,
113
+                [
114
+                    'body' => [
115
+                        'url' => $source,
116
+                        'token' => $token,
117
+                        'format' => 'json',
118
+                    ],
119
+                    'timeout' => 3,
120
+                    'connect_timeout' => 3,
121
+                    'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
122
+                ]
123
+            );
124
+
125
+            $status = $result->getStatusCode();
126
+        } catch (ClientException $e) {
127
+            $status = $e->getCode();
128
+            if ($status === Http::STATUS_FORBIDDEN) {
129
+                $this->logger->info($target . ' refused to ask for a shared secret.');
130
+            } else {
131
+                $this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage());
132
+            }
133
+        } catch (RequestException $e) {
134
+            $status = -1; // There is no status code if we could not connect
135
+            $this->logger->info('Could not connect to ' . $target);
136
+        } catch (\Throwable $e) {
137
+            $status = Http::STATUS_INTERNAL_SERVER_ERROR;
138
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
139
+        }
140
+
141
+        // if we received a unexpected response we try again later
142
+        if (
143
+            $status !== Http::STATUS_OK
144
+            && ($status !== Http::STATUS_FORBIDDEN || $this->getAttempt($argument) < 5)
145
+        ) {
146
+            $this->retainJob = true;
147
+        }
148
+    }
149
+
150
+    /**
151
+     * re-add background job
152
+     */
153
+    protected function reAddJob(array $argument): void {
154
+        $url = $argument['url'];
155
+        $created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
156
+        $token = $argument['token'];
157
+        $attempt = $this->getAttempt($argument) + 1;
158
+
159
+        $this->jobList->add(
160
+            RequestSharedSecret::class,
161
+            [
162
+                'url' => $url,
163
+                'token' => $token,
164
+                'created' => $created,
165
+                'attempt' => $attempt
166
+            ]
167
+        );
168
+    }
169
+
170
+    protected function getAttempt(array $argument): int {
171
+        return $argument['attempt'] ?? 0;
172
+    }
173 173
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
 	 */
87 87
 	protected function run($argument) {
88 88
 		$target = $argument['url'];
89
-		$created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
89
+		$created = isset($argument['created']) ? (int) $argument['created'] : $this->time->getTime();
90 90
 		$currentTime = $this->time->getTime();
91 91
 		$source = $this->urlGenerator->getAbsoluteURL('/');
92 92
 		$source = rtrim($source, '/');
@@ -105,7 +105,7 @@  discard block
 block discarded – undo
105 105
 		$endPoint = $endPoints['shared-secret'] ?? $this->defaultEndPoint;
106 106
 
107 107
 		// make sure that we have a well formatted url
108
-		$url = rtrim($target, '/') . '/' . trim($endPoint, '/');
108
+		$url = rtrim($target, '/').'/'.trim($endPoint, '/');
109 109
 
110 110
 		try {
111 111
 			$result = $this->httpClient->post(
@@ -126,13 +126,13 @@  discard block
 block discarded – undo
126 126
 		} catch (ClientException $e) {
127 127
 			$status = $e->getCode();
128 128
 			if ($status === Http::STATUS_FORBIDDEN) {
129
-				$this->logger->info($target . ' refused to ask for a shared secret.');
129
+				$this->logger->info($target.' refused to ask for a shared secret.');
130 130
 			} else {
131
-				$this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage());
131
+				$this->logger->info($target.' responded with a '.$status.' containing: '.$e->getMessage());
132 132
 			}
133 133
 		} catch (RequestException $e) {
134 134
 			$status = -1; // There is no status code if we could not connect
135
-			$this->logger->info('Could not connect to ' . $target);
135
+			$this->logger->info('Could not connect to '.$target);
136 136
 		} catch (\Throwable $e) {
137 137
 			$status = Http::STATUS_INTERNAL_SERVER_ERROR;
138 138
 			$this->logger->error($e->getMessage(), ['exception' => $e]);
@@ -152,7 +152,7 @@  discard block
 block discarded – undo
152 152
 	 */
153 153
 	protected function reAddJob(array $argument): void {
154 154
 		$url = $argument['url'];
155
-		$created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
155
+		$created = isset($argument['created']) ? (int) $argument['created'] : $this->time->getTime();
156 156
 		$token = $argument['token'];
157 157
 		$attempt = $this->getAttempt($argument) + 1;
158 158
 
Please login to merge, or discard this patch.
apps/federation/lib/BackgroundJob/GetSharedSecret.php 2 patches
Indentation   +144 added lines, -144 removed lines patch added patch discarded remove patch
@@ -30,148 +30,148 @@
 block discarded – undo
30 30
  * @package OCA\Federation\Backgroundjob
31 31
  */
32 32
 class GetSharedSecret extends Job {
33
-	private IClient $httpClient;
34
-	protected bool $retainJob = false;
35
-	private string $defaultEndPoint = '/ocs/v2.php/apps/federation/api/v1/shared-secret';
36
-	/** 30 day = 2592000sec */
37
-	private int $maxLifespan = 2592000;
38
-
39
-	public function __construct(
40
-		IClientService $httpClientService,
41
-		private IURLGenerator $urlGenerator,
42
-		private IJobList $jobList,
43
-		private TrustedServers $trustedServers,
44
-		private LoggerInterface $logger,
45
-		private IDiscoveryService $ocsDiscoveryService,
46
-		ITimeFactory $timeFactory,
47
-		private IConfig $config,
48
-	) {
49
-		parent::__construct($timeFactory);
50
-		$this->httpClient = $httpClientService->newClient();
51
-	}
52
-
53
-	/**
54
-	 * Run the job, then remove it from the joblist
55
-	 */
56
-	public function start(IJobList $jobList): void {
57
-		$target = $this->argument['url'];
58
-		// only execute if target is still in the list of trusted domains
59
-		if ($this->trustedServers->isTrustedServer($target)) {
60
-			$this->parentStart($jobList);
61
-		}
62
-
63
-		$jobList->remove($this, $this->argument);
64
-
65
-		if ($this->retainJob) {
66
-			$this->reAddJob($this->argument);
67
-		}
68
-	}
69
-
70
-	protected function parentStart(IJobList $jobList): void {
71
-		parent::start($jobList);
72
-	}
73
-
74
-	protected function run($argument) {
75
-		$target = $argument['url'];
76
-		$created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
77
-		$currentTime = $this->time->getTime();
78
-		$source = $this->urlGenerator->getAbsoluteURL('/');
79
-		$source = rtrim($source, '/');
80
-		$token = $argument['token'];
81
-
82
-		// kill job after 30 days of trying
83
-		$deadline = $currentTime - $this->maxLifespan;
84
-		if ($created < $deadline) {
85
-			$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.");
86
-			$this->retainJob = false;
87
-			$this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
88
-			return;
89
-		}
90
-
91
-		$endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING');
92
-		$endPoint = $endPoints['shared-secret'] ?? $this->defaultEndPoint;
93
-
94
-		// make sure that we have a well formatted url
95
-		$url = rtrim($target, '/') . '/' . trim($endPoint, '/');
96
-
97
-		$result = null;
98
-		try {
99
-			$result = $this->httpClient->get(
100
-				$url,
101
-				[
102
-					'query' =>
103
-						[
104
-							'url' => $source,
105
-							'token' => $token,
106
-							'format' => 'json',
107
-						],
108
-					'timeout' => 3,
109
-					'connect_timeout' => 3,
110
-					'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
111
-				]
112
-			);
113
-
114
-			$status = $result->getStatusCode();
115
-		} catch (ClientException $e) {
116
-			$status = $e->getCode();
117
-			if ($status === Http::STATUS_FORBIDDEN) {
118
-				$this->logger->info($target . ' refused to exchange a shared secret with you.');
119
-			} else {
120
-				$this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage());
121
-			}
122
-		} catch (RequestException $e) {
123
-			$status = -1; // There is no status code if we could not connect
124
-			$this->logger->info('Could not connect to ' . $target, [
125
-				'exception' => $e,
126
-			]);
127
-		} catch (\Throwable $e) {
128
-			$status = Http::STATUS_INTERNAL_SERVER_ERROR;
129
-			$this->logger->error($e->getMessage(), [
130
-				'exception' => $e,
131
-			]);
132
-		}
133
-
134
-		// if we received a unexpected response we try again later
135
-		if (
136
-			$status !== Http::STATUS_OK
137
-			&& $status !== Http::STATUS_FORBIDDEN
138
-		) {
139
-			$this->retainJob = true;
140
-		}
141
-
142
-		if ($status === Http::STATUS_OK && $result instanceof IResponse) {
143
-			$body = $result->getBody();
144
-			$result = json_decode($body, true);
145
-			if (isset($result['ocs']['data']['sharedSecret'])) {
146
-				$this->trustedServers->addSharedSecret(
147
-					$target,
148
-					$result['ocs']['data']['sharedSecret']
149
-				);
150
-			} else {
151
-				$this->logger->error(
152
-					'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . $body
153
-				);
154
-				$this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
155
-			}
156
-		}
157
-	}
158
-
159
-	/**
160
-	 * Re-add background job
161
-	 *
162
-	 * @param array $argument
163
-	 */
164
-	protected function reAddJob(array $argument): void {
165
-		$url = $argument['url'];
166
-		$created = $argument['created'] ?? $this->time->getTime();
167
-		$token = $argument['token'];
168
-		$this->jobList->add(
169
-			GetSharedSecret::class,
170
-			[
171
-				'url' => $url,
172
-				'token' => $token,
173
-				'created' => $created
174
-			]
175
-		);
176
-	}
33
+    private IClient $httpClient;
34
+    protected bool $retainJob = false;
35
+    private string $defaultEndPoint = '/ocs/v2.php/apps/federation/api/v1/shared-secret';
36
+    /** 30 day = 2592000sec */
37
+    private int $maxLifespan = 2592000;
38
+
39
+    public function __construct(
40
+        IClientService $httpClientService,
41
+        private IURLGenerator $urlGenerator,
42
+        private IJobList $jobList,
43
+        private TrustedServers $trustedServers,
44
+        private LoggerInterface $logger,
45
+        private IDiscoveryService $ocsDiscoveryService,
46
+        ITimeFactory $timeFactory,
47
+        private IConfig $config,
48
+    ) {
49
+        parent::__construct($timeFactory);
50
+        $this->httpClient = $httpClientService->newClient();
51
+    }
52
+
53
+    /**
54
+     * Run the job, then remove it from the joblist
55
+     */
56
+    public function start(IJobList $jobList): void {
57
+        $target = $this->argument['url'];
58
+        // only execute if target is still in the list of trusted domains
59
+        if ($this->trustedServers->isTrustedServer($target)) {
60
+            $this->parentStart($jobList);
61
+        }
62
+
63
+        $jobList->remove($this, $this->argument);
64
+
65
+        if ($this->retainJob) {
66
+            $this->reAddJob($this->argument);
67
+        }
68
+    }
69
+
70
+    protected function parentStart(IJobList $jobList): void {
71
+        parent::start($jobList);
72
+    }
73
+
74
+    protected function run($argument) {
75
+        $target = $argument['url'];
76
+        $created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
77
+        $currentTime = $this->time->getTime();
78
+        $source = $this->urlGenerator->getAbsoluteURL('/');
79
+        $source = rtrim($source, '/');
80
+        $token = $argument['token'];
81
+
82
+        // kill job after 30 days of trying
83
+        $deadline = $currentTime - $this->maxLifespan;
84
+        if ($created < $deadline) {
85
+            $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.");
86
+            $this->retainJob = false;
87
+            $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
88
+            return;
89
+        }
90
+
91
+        $endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING');
92
+        $endPoint = $endPoints['shared-secret'] ?? $this->defaultEndPoint;
93
+
94
+        // make sure that we have a well formatted url
95
+        $url = rtrim($target, '/') . '/' . trim($endPoint, '/');
96
+
97
+        $result = null;
98
+        try {
99
+            $result = $this->httpClient->get(
100
+                $url,
101
+                [
102
+                    'query' =>
103
+                        [
104
+                            'url' => $source,
105
+                            'token' => $token,
106
+                            'format' => 'json',
107
+                        ],
108
+                    'timeout' => 3,
109
+                    'connect_timeout' => 3,
110
+                    'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
111
+                ]
112
+            );
113
+
114
+            $status = $result->getStatusCode();
115
+        } catch (ClientException $e) {
116
+            $status = $e->getCode();
117
+            if ($status === Http::STATUS_FORBIDDEN) {
118
+                $this->logger->info($target . ' refused to exchange a shared secret with you.');
119
+            } else {
120
+                $this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage());
121
+            }
122
+        } catch (RequestException $e) {
123
+            $status = -1; // There is no status code if we could not connect
124
+            $this->logger->info('Could not connect to ' . $target, [
125
+                'exception' => $e,
126
+            ]);
127
+        } catch (\Throwable $e) {
128
+            $status = Http::STATUS_INTERNAL_SERVER_ERROR;
129
+            $this->logger->error($e->getMessage(), [
130
+                'exception' => $e,
131
+            ]);
132
+        }
133
+
134
+        // if we received a unexpected response we try again later
135
+        if (
136
+            $status !== Http::STATUS_OK
137
+            && $status !== Http::STATUS_FORBIDDEN
138
+        ) {
139
+            $this->retainJob = true;
140
+        }
141
+
142
+        if ($status === Http::STATUS_OK && $result instanceof IResponse) {
143
+            $body = $result->getBody();
144
+            $result = json_decode($body, true);
145
+            if (isset($result['ocs']['data']['sharedSecret'])) {
146
+                $this->trustedServers->addSharedSecret(
147
+                    $target,
148
+                    $result['ocs']['data']['sharedSecret']
149
+                );
150
+            } else {
151
+                $this->logger->error(
152
+                    'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . $body
153
+                );
154
+                $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
155
+            }
156
+        }
157
+    }
158
+
159
+    /**
160
+     * Re-add background job
161
+     *
162
+     * @param array $argument
163
+     */
164
+    protected function reAddJob(array $argument): void {
165
+        $url = $argument['url'];
166
+        $created = $argument['created'] ?? $this->time->getTime();
167
+        $token = $argument['token'];
168
+        $this->jobList->add(
169
+            GetSharedSecret::class,
170
+            [
171
+                'url' => $url,
172
+                'token' => $token,
173
+                'created' => $created
174
+            ]
175
+        );
176
+    }
177 177
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -73,7 +73,7 @@  discard block
 block discarded – undo
73 73
 
74 74
 	protected function run($argument) {
75 75
 		$target = $argument['url'];
76
-		$created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
76
+		$created = isset($argument['created']) ? (int) $argument['created'] : $this->time->getTime();
77 77
 		$currentTime = $this->time->getTime();
78 78
 		$source = $this->urlGenerator->getAbsoluteURL('/');
79 79
 		$source = rtrim($source, '/');
@@ -92,7 +92,7 @@  discard block
 block discarded – undo
92 92
 		$endPoint = $endPoints['shared-secret'] ?? $this->defaultEndPoint;
93 93
 
94 94
 		// make sure that we have a well formatted url
95
-		$url = rtrim($target, '/') . '/' . trim($endPoint, '/');
95
+		$url = rtrim($target, '/').'/'.trim($endPoint, '/');
96 96
 
97 97
 		$result = null;
98 98
 		try {
@@ -115,13 +115,13 @@  discard block
 block discarded – undo
115 115
 		} catch (ClientException $e) {
116 116
 			$status = $e->getCode();
117 117
 			if ($status === Http::STATUS_FORBIDDEN) {
118
-				$this->logger->info($target . ' refused to exchange a shared secret with you.');
118
+				$this->logger->info($target.' refused to exchange a shared secret with you.');
119 119
 			} else {
120
-				$this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage());
120
+				$this->logger->info($target.' responded with a '.$status.' containing: '.$e->getMessage());
121 121
 			}
122 122
 		} catch (RequestException $e) {
123 123
 			$status = -1; // There is no status code if we could not connect
124
-			$this->logger->info('Could not connect to ' . $target, [
124
+			$this->logger->info('Could not connect to '.$target, [
125 125
 				'exception' => $e,
126 126
 			]);
127 127
 		} catch (\Throwable $e) {
@@ -149,7 +149,7 @@  discard block
 block discarded – undo
149 149
 				);
150 150
 			} else {
151 151
 				$this->logger->error(
152
-					'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . $body
152
+					'remote server "'.$target.'"" does not return a valid shared secret. Received data: '.$body
153 153
 				);
154 154
 				$this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
155 155
 			}
Please login to merge, or discard this patch.
apps/federation/lib/Controller/OCSAuthAPIController.php 2 patches
Indentation   +133 added lines, -133 removed lines patch added patch discarded remove patch
@@ -33,137 +33,137 @@
 block discarded – undo
33 33
  */
34 34
 #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
35 35
 class OCSAuthAPIController extends OCSController {
36
-	public function __construct(
37
-		string $appName,
38
-		IRequest $request,
39
-		private ISecureRandom $secureRandom,
40
-		private IJobList $jobList,
41
-		private TrustedServers $trustedServers,
42
-		private DbHandler $dbHandler,
43
-		private LoggerInterface $logger,
44
-		private ITimeFactory $timeFactory,
45
-		private IThrottler $throttler,
46
-	) {
47
-		parent::__construct($appName, $request);
48
-	}
49
-
50
-	/**
51
-	 * Request received to ask remote server for a shared secret, for legacy end-points
52
-	 *
53
-	 * @param string $url URL of the server
54
-	 * @param string $token Token of the server
55
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
56
-	 * @throws OCSForbiddenException Requesting shared secret is not allowed
57
-	 *
58
-	 * 200: Shared secret requested successfully
59
-	 */
60
-	#[NoCSRFRequired]
61
-	#[PublicPage]
62
-	#[BruteForceProtection(action: 'federationSharedSecret')]
63
-	public function requestSharedSecretLegacy(string $url, string $token): DataResponse {
64
-		return $this->requestSharedSecret($url, $token);
65
-	}
66
-
67
-
68
-	/**
69
-	 * Create shared secret and return it, for legacy end-points
70
-	 *
71
-	 * @param string $url URL of the server
72
-	 * @param string $token Token of the server
73
-	 * @return DataResponse<Http::STATUS_OK, array{sharedSecret: string}, array{}>
74
-	 * @throws OCSForbiddenException Getting shared secret is not allowed
75
-	 *
76
-	 * 200: Shared secret returned
77
-	 */
78
-	#[NoCSRFRequired]
79
-	#[PublicPage]
80
-	#[BruteForceProtection(action: 'federationSharedSecret')]
81
-	public function getSharedSecretLegacy(string $url, string $token): DataResponse {
82
-		return $this->getSharedSecret($url, $token);
83
-	}
84
-
85
-	/**
86
-	 * Request received to ask remote server for a shared secret
87
-	 *
88
-	 * @param string $url URL of the server
89
-	 * @param string $token Token of the server
90
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
91
-	 * @throws OCSForbiddenException Requesting shared secret is not allowed
92
-	 *
93
-	 * 200: Shared secret requested successfully
94
-	 */
95
-	#[NoCSRFRequired]
96
-	#[PublicPage]
97
-	#[BruteForceProtection(action: 'federationSharedSecret')]
98
-	public function requestSharedSecret(string $url, string $token): DataResponse {
99
-		if ($this->trustedServers->isTrustedServer($url) === false) {
100
-			$this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
101
-			$this->logger->error('remote server not trusted (' . $url . ') while requesting shared secret');
102
-			throw new OCSForbiddenException();
103
-		}
104
-
105
-		// if both server initiated the exchange of the shared secret the greater
106
-		// token wins
107
-		$localToken = $this->dbHandler->getToken($url);
108
-		if (strcmp($localToken, $token) > 0) {
109
-			$this->logger->info(
110
-				'remote server (' . $url . ') presented lower token. We will initiate the exchange of the shared secret.'
111
-			);
112
-			throw new OCSForbiddenException();
113
-		}
114
-
115
-		$this->jobList->add(
116
-			'OCA\Federation\BackgroundJob\GetSharedSecret',
117
-			[
118
-				'url' => $url,
119
-				'token' => $token,
120
-				'created' => $this->timeFactory->getTime()
121
-			]
122
-		);
123
-
124
-		return new DataResponse();
125
-	}
126
-
127
-	/**
128
-	 * Create shared secret and return it
129
-	 *
130
-	 * @param string $url URL of the server
131
-	 * @param string $token Token of the server
132
-	 * @return DataResponse<Http::STATUS_OK, array{sharedSecret: string}, array{}>
133
-	 * @throws OCSForbiddenException Getting shared secret is not allowed
134
-	 *
135
-	 * 200: Shared secret returned
136
-	 */
137
-	#[NoCSRFRequired]
138
-	#[PublicPage]
139
-	#[BruteForceProtection(action: 'federationSharedSecret')]
140
-	public function getSharedSecret(string $url, string $token): DataResponse {
141
-		if ($this->trustedServers->isTrustedServer($url) === false) {
142
-			$this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
143
-			$this->logger->error('remote server not trusted (' . $url . ') while getting shared secret');
144
-			throw new OCSForbiddenException();
145
-		}
146
-
147
-		if ($this->isValidToken($url, $token) === false) {
148
-			$this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
149
-			$expectedToken = $this->dbHandler->getToken($url);
150
-			$this->logger->error(
151
-				'remote server (' . $url . ') didn\'t send a valid token (got "' . $token . '" but expected "' . $expectedToken . '") while getting shared secret'
152
-			);
153
-			throw new OCSForbiddenException();
154
-		}
155
-
156
-		$sharedSecret = $this->secureRandom->generate(32);
157
-
158
-		$this->trustedServers->addSharedSecret($url, $sharedSecret);
159
-
160
-		return new DataResponse([
161
-			'sharedSecret' => $sharedSecret
162
-		]);
163
-	}
164
-
165
-	protected function isValidToken(string $url, string $token): bool {
166
-		$storedToken = $this->dbHandler->getToken($url);
167
-		return hash_equals($storedToken, $token);
168
-	}
36
+    public function __construct(
37
+        string $appName,
38
+        IRequest $request,
39
+        private ISecureRandom $secureRandom,
40
+        private IJobList $jobList,
41
+        private TrustedServers $trustedServers,
42
+        private DbHandler $dbHandler,
43
+        private LoggerInterface $logger,
44
+        private ITimeFactory $timeFactory,
45
+        private IThrottler $throttler,
46
+    ) {
47
+        parent::__construct($appName, $request);
48
+    }
49
+
50
+    /**
51
+     * Request received to ask remote server for a shared secret, for legacy end-points
52
+     *
53
+     * @param string $url URL of the server
54
+     * @param string $token Token of the server
55
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
56
+     * @throws OCSForbiddenException Requesting shared secret is not allowed
57
+     *
58
+     * 200: Shared secret requested successfully
59
+     */
60
+    #[NoCSRFRequired]
61
+    #[PublicPage]
62
+    #[BruteForceProtection(action: 'federationSharedSecret')]
63
+    public function requestSharedSecretLegacy(string $url, string $token): DataResponse {
64
+        return $this->requestSharedSecret($url, $token);
65
+    }
66
+
67
+
68
+    /**
69
+     * Create shared secret and return it, for legacy end-points
70
+     *
71
+     * @param string $url URL of the server
72
+     * @param string $token Token of the server
73
+     * @return DataResponse<Http::STATUS_OK, array{sharedSecret: string}, array{}>
74
+     * @throws OCSForbiddenException Getting shared secret is not allowed
75
+     *
76
+     * 200: Shared secret returned
77
+     */
78
+    #[NoCSRFRequired]
79
+    #[PublicPage]
80
+    #[BruteForceProtection(action: 'federationSharedSecret')]
81
+    public function getSharedSecretLegacy(string $url, string $token): DataResponse {
82
+        return $this->getSharedSecret($url, $token);
83
+    }
84
+
85
+    /**
86
+     * Request received to ask remote server for a shared secret
87
+     *
88
+     * @param string $url URL of the server
89
+     * @param string $token Token of the server
90
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
91
+     * @throws OCSForbiddenException Requesting shared secret is not allowed
92
+     *
93
+     * 200: Shared secret requested successfully
94
+     */
95
+    #[NoCSRFRequired]
96
+    #[PublicPage]
97
+    #[BruteForceProtection(action: 'federationSharedSecret')]
98
+    public function requestSharedSecret(string $url, string $token): DataResponse {
99
+        if ($this->trustedServers->isTrustedServer($url) === false) {
100
+            $this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
101
+            $this->logger->error('remote server not trusted (' . $url . ') while requesting shared secret');
102
+            throw new OCSForbiddenException();
103
+        }
104
+
105
+        // if both server initiated the exchange of the shared secret the greater
106
+        // token wins
107
+        $localToken = $this->dbHandler->getToken($url);
108
+        if (strcmp($localToken, $token) > 0) {
109
+            $this->logger->info(
110
+                'remote server (' . $url . ') presented lower token. We will initiate the exchange of the shared secret.'
111
+            );
112
+            throw new OCSForbiddenException();
113
+        }
114
+
115
+        $this->jobList->add(
116
+            'OCA\Federation\BackgroundJob\GetSharedSecret',
117
+            [
118
+                'url' => $url,
119
+                'token' => $token,
120
+                'created' => $this->timeFactory->getTime()
121
+            ]
122
+        );
123
+
124
+        return new DataResponse();
125
+    }
126
+
127
+    /**
128
+     * Create shared secret and return it
129
+     *
130
+     * @param string $url URL of the server
131
+     * @param string $token Token of the server
132
+     * @return DataResponse<Http::STATUS_OK, array{sharedSecret: string}, array{}>
133
+     * @throws OCSForbiddenException Getting shared secret is not allowed
134
+     *
135
+     * 200: Shared secret returned
136
+     */
137
+    #[NoCSRFRequired]
138
+    #[PublicPage]
139
+    #[BruteForceProtection(action: 'federationSharedSecret')]
140
+    public function getSharedSecret(string $url, string $token): DataResponse {
141
+        if ($this->trustedServers->isTrustedServer($url) === false) {
142
+            $this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
143
+            $this->logger->error('remote server not trusted (' . $url . ') while getting shared secret');
144
+            throw new OCSForbiddenException();
145
+        }
146
+
147
+        if ($this->isValidToken($url, $token) === false) {
148
+            $this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
149
+            $expectedToken = $this->dbHandler->getToken($url);
150
+            $this->logger->error(
151
+                'remote server (' . $url . ') didn\'t send a valid token (got "' . $token . '" but expected "' . $expectedToken . '") while getting shared secret'
152
+            );
153
+            throw new OCSForbiddenException();
154
+        }
155
+
156
+        $sharedSecret = $this->secureRandom->generate(32);
157
+
158
+        $this->trustedServers->addSharedSecret($url, $sharedSecret);
159
+
160
+        return new DataResponse([
161
+            'sharedSecret' => $sharedSecret
162
+        ]);
163
+    }
164
+
165
+    protected function isValidToken(string $url, string $token): bool {
166
+        $storedToken = $this->dbHandler->getToken($url);
167
+        return hash_equals($storedToken, $token);
168
+    }
169 169
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
 	public function requestSharedSecret(string $url, string $token): DataResponse {
99 99
 		if ($this->trustedServers->isTrustedServer($url) === false) {
100 100
 			$this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
101
-			$this->logger->error('remote server not trusted (' . $url . ') while requesting shared secret');
101
+			$this->logger->error('remote server not trusted ('.$url.') while requesting shared secret');
102 102
 			throw new OCSForbiddenException();
103 103
 		}
104 104
 
@@ -107,7 +107,7 @@  discard block
 block discarded – undo
107 107
 		$localToken = $this->dbHandler->getToken($url);
108 108
 		if (strcmp($localToken, $token) > 0) {
109 109
 			$this->logger->info(
110
-				'remote server (' . $url . ') presented lower token. We will initiate the exchange of the shared secret.'
110
+				'remote server ('.$url.') presented lower token. We will initiate the exchange of the shared secret.'
111 111
 			);
112 112
 			throw new OCSForbiddenException();
113 113
 		}
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
 	public function getSharedSecret(string $url, string $token): DataResponse {
141 141
 		if ($this->trustedServers->isTrustedServer($url) === false) {
142 142
 			$this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
143
-			$this->logger->error('remote server not trusted (' . $url . ') while getting shared secret');
143
+			$this->logger->error('remote server not trusted ('.$url.') while getting shared secret');
144 144
 			throw new OCSForbiddenException();
145 145
 		}
146 146
 
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
 			$this->throttler->registerAttempt('federationSharedSecret', $this->request->getRemoteAddress());
149 149
 			$expectedToken = $this->dbHandler->getToken($url);
150 150
 			$this->logger->error(
151
-				'remote server (' . $url . ') didn\'t send a valid token (got "' . $token . '" but expected "' . $expectedToken . '") while getting shared secret'
151
+				'remote server ('.$url.') didn\'t send a valid token (got "'.$token.'" but expected "'.$expectedToken.'") while getting shared secret'
152 152
 			);
153 153
 			throw new OCSForbiddenException();
154 154
 		}
Please login to merge, or discard this patch.
apps/federation/lib/TrustedServers.php 1 patch
Indentation   +183 added lines, -183 removed lines patch added patch discarded remove patch
@@ -22,187 +22,187 @@
 block discarded – undo
22 22
 
23 23
 class TrustedServers {
24 24
 
25
-	/** after a user list was exchanged at least once successfully */
26
-	public const STATUS_OK = 1;
27
-	/** waiting for shared secret or initial user list exchange */
28
-	public const STATUS_PENDING = 2;
29
-	/** something went wrong, misconfigured server, software bug,... user interaction needed */
30
-	public const STATUS_FAILURE = 3;
31
-	/** remote server revoked access */
32
-	public const STATUS_ACCESS_REVOKED = 4;
33
-
34
-	/** @var list<array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}>|null */
35
-	private ?array $trustedServersCache = null;
36
-
37
-	public function __construct(
38
-		private DbHandler $dbHandler,
39
-		private IClientService $httpClientService,
40
-		private LoggerInterface $logger,
41
-		private IJobList $jobList,
42
-		private ISecureRandom $secureRandom,
43
-		private IConfig $config,
44
-		private IEventDispatcher $dispatcher,
45
-		private ITimeFactory $timeFactory,
46
-	) {
47
-	}
48
-
49
-	/**
50
-	 * Add server to the list of trusted servers
51
-	 */
52
-	public function addServer(string $url): int {
53
-		$url = $this->updateProtocol($url);
54
-		$result = $this->dbHandler->addServer($url);
55
-		if ($result) {
56
-			$token = $this->secureRandom->generate(16);
57
-			$this->dbHandler->addToken($url, $token);
58
-			$this->jobList->add(
59
-				RequestSharedSecret::class,
60
-				[
61
-					'url' => $url,
62
-					'token' => $token,
63
-					'created' => $this->timeFactory->getTime()
64
-				]
65
-			);
66
-		}
67
-
68
-		return $result;
69
-	}
70
-
71
-	/**
72
-	 * Get shared secret for the given server
73
-	 */
74
-	public function getSharedSecret(string $url): string {
75
-		return $this->dbHandler->getSharedSecret($url);
76
-	}
77
-
78
-	/**
79
-	 * Add shared secret for the given server
80
-	 */
81
-	public function addSharedSecret(string $url, string $sharedSecret): void {
82
-		$this->dbHandler->addSharedSecret($url, $sharedSecret);
83
-	}
84
-
85
-	/**
86
-	 * Remove server from the list of trusted servers
87
-	 */
88
-	public function removeServer(int $id): void {
89
-		$server = $this->dbHandler->getServerById($id);
90
-		$this->dbHandler->removeServer($id);
91
-		$this->dispatcher->dispatchTyped(new TrustedServerRemovedEvent($server['url_hash']));
92
-
93
-	}
94
-
95
-	/**
96
-	 * Get all trusted servers
97
-	 *
98
-	 * @return list<array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}>
99
-	 * @throws \Exception
100
-	 */
101
-	public function getServers(): ?array {
102
-		if ($this->trustedServersCache === null) {
103
-			$this->trustedServersCache = $this->dbHandler->getAllServer();
104
-		}
105
-		return $this->trustedServersCache;
106
-	}
107
-
108
-	/**
109
-	 * Get a trusted server
110
-	 *
111
-	 * @return array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}
112
-	 * @throws Exception
113
-	 */
114
-	public function getServer(int $id): ?array {
115
-		if ($this->trustedServersCache === null) {
116
-			$this->trustedServersCache = $this->dbHandler->getAllServer();
117
-		}
118
-
119
-		$server = array_filter($this->trustedServersCache, fn ($server) => $server['id'] === $id);
120
-		if (empty($server)) {
121
-			throw new \Exception('No server found with ID: ' . $id);
122
-		}
123
-
124
-		return $server[0];
125
-	}
126
-
127
-	/**
128
-	 * Check if given server is a trusted Nextcloud server
129
-	 */
130
-	public function isTrustedServer(string $url): bool {
131
-		return $this->dbHandler->serverExists($url);
132
-	}
133
-
134
-	/**
135
-	 * Set server status
136
-	 */
137
-	public function setServerStatus(string $url, int $status): void {
138
-		$this->dbHandler->setServerStatus($url, $status);
139
-	}
140
-
141
-	/**
142
-	 * Get server status
143
-	 */
144
-	public function getServerStatus(string $url): int {
145
-		return $this->dbHandler->getServerStatus($url);
146
-	}
147
-
148
-	/**
149
-	 * Check if URL point to a ownCloud/Nextcloud server
150
-	 */
151
-	public function isNextcloudServer(string $url): bool {
152
-		$isValidNextcloud = false;
153
-		$client = $this->httpClientService->newClient();
154
-		try {
155
-			$result = $client->get(
156
-				$url . '/status.php',
157
-				[
158
-					'timeout' => 3,
159
-					'connect_timeout' => 3,
160
-					'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
161
-				]
162
-			);
163
-			if ($result->getStatusCode() === Http::STATUS_OK) {
164
-				$body = $result->getBody();
165
-				if (is_resource($body)) {
166
-					$body = stream_get_contents($body) ?: '';
167
-				}
168
-				$isValidNextcloud = $this->checkNextcloudVersion($body);
169
-			}
170
-		} catch (\Exception $e) {
171
-			$this->logger->error('No Nextcloud server.', [
172
-				'exception' => $e,
173
-			]);
174
-			return false;
175
-		}
176
-
177
-		return $isValidNextcloud;
178
-	}
179
-
180
-	/**
181
-	 * Check if ownCloud/Nextcloud version is >= 9.0
182
-	 * @throws HintException
183
-	 */
184
-	protected function checkNextcloudVersion(string $status): bool {
185
-		$decoded = json_decode($status, true);
186
-		if (!empty($decoded) && isset($decoded['version'])) {
187
-			if (!version_compare($decoded['version'], '9.0.0', '>=')) {
188
-				throw new HintException('Remote server version is too low. 9.0 is required.');
189
-			}
190
-			return true;
191
-		}
192
-		return false;
193
-	}
194
-
195
-	/**
196
-	 * Check if the URL contain a protocol, if not add https
197
-	 */
198
-	protected function updateProtocol(string $url): string {
199
-		if (
200
-			strpos($url, 'https://') === 0
201
-			|| strpos($url, 'http://') === 0
202
-		) {
203
-			return $url;
204
-		}
205
-
206
-		return 'https://' . $url;
207
-	}
25
+    /** after a user list was exchanged at least once successfully */
26
+    public const STATUS_OK = 1;
27
+    /** waiting for shared secret or initial user list exchange */
28
+    public const STATUS_PENDING = 2;
29
+    /** something went wrong, misconfigured server, software bug,... user interaction needed */
30
+    public const STATUS_FAILURE = 3;
31
+    /** remote server revoked access */
32
+    public const STATUS_ACCESS_REVOKED = 4;
33
+
34
+    /** @var list<array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}>|null */
35
+    private ?array $trustedServersCache = null;
36
+
37
+    public function __construct(
38
+        private DbHandler $dbHandler,
39
+        private IClientService $httpClientService,
40
+        private LoggerInterface $logger,
41
+        private IJobList $jobList,
42
+        private ISecureRandom $secureRandom,
43
+        private IConfig $config,
44
+        private IEventDispatcher $dispatcher,
45
+        private ITimeFactory $timeFactory,
46
+    ) {
47
+    }
48
+
49
+    /**
50
+     * Add server to the list of trusted servers
51
+     */
52
+    public function addServer(string $url): int {
53
+        $url = $this->updateProtocol($url);
54
+        $result = $this->dbHandler->addServer($url);
55
+        if ($result) {
56
+            $token = $this->secureRandom->generate(16);
57
+            $this->dbHandler->addToken($url, $token);
58
+            $this->jobList->add(
59
+                RequestSharedSecret::class,
60
+                [
61
+                    'url' => $url,
62
+                    'token' => $token,
63
+                    'created' => $this->timeFactory->getTime()
64
+                ]
65
+            );
66
+        }
67
+
68
+        return $result;
69
+    }
70
+
71
+    /**
72
+     * Get shared secret for the given server
73
+     */
74
+    public function getSharedSecret(string $url): string {
75
+        return $this->dbHandler->getSharedSecret($url);
76
+    }
77
+
78
+    /**
79
+     * Add shared secret for the given server
80
+     */
81
+    public function addSharedSecret(string $url, string $sharedSecret): void {
82
+        $this->dbHandler->addSharedSecret($url, $sharedSecret);
83
+    }
84
+
85
+    /**
86
+     * Remove server from the list of trusted servers
87
+     */
88
+    public function removeServer(int $id): void {
89
+        $server = $this->dbHandler->getServerById($id);
90
+        $this->dbHandler->removeServer($id);
91
+        $this->dispatcher->dispatchTyped(new TrustedServerRemovedEvent($server['url_hash']));
92
+
93
+    }
94
+
95
+    /**
96
+     * Get all trusted servers
97
+     *
98
+     * @return list<array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}>
99
+     * @throws \Exception
100
+     */
101
+    public function getServers(): ?array {
102
+        if ($this->trustedServersCache === null) {
103
+            $this->trustedServersCache = $this->dbHandler->getAllServer();
104
+        }
105
+        return $this->trustedServersCache;
106
+    }
107
+
108
+    /**
109
+     * Get a trusted server
110
+     *
111
+     * @return array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}
112
+     * @throws Exception
113
+     */
114
+    public function getServer(int $id): ?array {
115
+        if ($this->trustedServersCache === null) {
116
+            $this->trustedServersCache = $this->dbHandler->getAllServer();
117
+        }
118
+
119
+        $server = array_filter($this->trustedServersCache, fn ($server) => $server['id'] === $id);
120
+        if (empty($server)) {
121
+            throw new \Exception('No server found with ID: ' . $id);
122
+        }
123
+
124
+        return $server[0];
125
+    }
126
+
127
+    /**
128
+     * Check if given server is a trusted Nextcloud server
129
+     */
130
+    public function isTrustedServer(string $url): bool {
131
+        return $this->dbHandler->serverExists($url);
132
+    }
133
+
134
+    /**
135
+     * Set server status
136
+     */
137
+    public function setServerStatus(string $url, int $status): void {
138
+        $this->dbHandler->setServerStatus($url, $status);
139
+    }
140
+
141
+    /**
142
+     * Get server status
143
+     */
144
+    public function getServerStatus(string $url): int {
145
+        return $this->dbHandler->getServerStatus($url);
146
+    }
147
+
148
+    /**
149
+     * Check if URL point to a ownCloud/Nextcloud server
150
+     */
151
+    public function isNextcloudServer(string $url): bool {
152
+        $isValidNextcloud = false;
153
+        $client = $this->httpClientService->newClient();
154
+        try {
155
+            $result = $client->get(
156
+                $url . '/status.php',
157
+                [
158
+                    'timeout' => 3,
159
+                    'connect_timeout' => 3,
160
+                    'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
161
+                ]
162
+            );
163
+            if ($result->getStatusCode() === Http::STATUS_OK) {
164
+                $body = $result->getBody();
165
+                if (is_resource($body)) {
166
+                    $body = stream_get_contents($body) ?: '';
167
+                }
168
+                $isValidNextcloud = $this->checkNextcloudVersion($body);
169
+            }
170
+        } catch (\Exception $e) {
171
+            $this->logger->error('No Nextcloud server.', [
172
+                'exception' => $e,
173
+            ]);
174
+            return false;
175
+        }
176
+
177
+        return $isValidNextcloud;
178
+    }
179
+
180
+    /**
181
+     * Check if ownCloud/Nextcloud version is >= 9.0
182
+     * @throws HintException
183
+     */
184
+    protected function checkNextcloudVersion(string $status): bool {
185
+        $decoded = json_decode($status, true);
186
+        if (!empty($decoded) && isset($decoded['version'])) {
187
+            if (!version_compare($decoded['version'], '9.0.0', '>=')) {
188
+                throw new HintException('Remote server version is too low. 9.0 is required.');
189
+            }
190
+            return true;
191
+        }
192
+        return false;
193
+    }
194
+
195
+    /**
196
+     * Check if the URL contain a protocol, if not add https
197
+     */
198
+    protected function updateProtocol(string $url): string {
199
+        if (
200
+            strpos($url, 'https://') === 0
201
+            || strpos($url, 'http://') === 0
202
+        ) {
203
+            return $url;
204
+        }
205
+
206
+        return 'https://' . $url;
207
+    }
208 208
 }
Please login to merge, or discard this patch.