@fleetbase/solid-engine 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ACL_SOLUTION.md +72 -0
- package/CSS_SCOPE_ISSUE.md +140 -0
- package/HOTFIX_SYNTAX_ERROR.md +100 -0
- package/LICENSE.md +651 -21
- package/MANUAL_ACL_SETUP.md +135 -0
- package/README.md +74 -27
- package/REFACTORING_SUMMARY.md +330 -0
- package/VERIFICATION_CHECKLIST.md +82 -0
- package/addon/components/modals/create-solid-folder.hbs +29 -0
- package/addon/components/modals/import-solid-resources.hbs +85 -0
- package/addon/controllers/data/content.js +17 -0
- package/addon/controllers/data/index.js +219 -0
- package/addon/controllers/home.js +84 -0
- package/addon/engine.js +1 -24
- package/addon/extension.js +26 -0
- package/addon/routes/data/content.js +11 -0
- package/addon/routes/data/index.js +17 -0
- package/addon/routes.js +2 -7
- package/addon/styles/solid-engine.css +1 -2
- package/addon/templates/account.hbs +3 -3
- package/addon/templates/application.hbs +2 -12
- package/addon/templates/data/content.hbs +48 -0
- package/addon/templates/{pods/explorer.hbs → data/index.hbs} +6 -5
- package/addon/templates/home.hbs +168 -10
- package/app/components/modals/{backup-pod.js → create-solid-folder.js} +1 -1
- package/app/components/modals/{resync-pod.js → import-solid-resources.js} +1 -1
- package/app/components/modals/{create-pod.js → setup-css-credentials.js} +1 -1
- package/composer.json +5 -11
- package/extension.json +2 -2
- package/index.js +0 -11
- package/package.json +9 -9
- package/server/migrations/2024_12_21_add_css_credentials_to_solid_identities_table.php +32 -0
- package/server/src/Client/OpenIDConnectClient.php +686 -15
- package/server/src/Client/SolidClient.php +104 -8
- package/server/src/Http/Controllers/DataController.php +261 -0
- package/server/src/Http/Controllers/OIDCController.php +42 -8
- package/server/src/Http/Controllers/SolidController.php +179 -85
- package/server/src/Models/SolidIdentity.php +13 -3
- package/server/src/Services/AclService.php +146 -0
- package/server/src/Services/PodService.php +863 -0
- package/server/src/Services/ResourceSyncService.php +336 -0
- package/server/src/Services/VehicleSyncService.php +289 -0
- package/server/src/Support/Utils.php +10 -0
- package/server/src/routes.php +25 -1
- package/addon/components/modals/backup-pod.hbs +0 -3
- package/addon/components/modals/create-pod.hbs +0 -5
- package/addon/components/modals/resync-pod.hbs +0 -3
- package/addon/controllers/pods/explorer/content.js +0 -12
- package/addon/controllers/pods/explorer.js +0 -149
- package/addon/controllers/pods/index/pod.js +0 -12
- package/addon/controllers/pods/index.js +0 -137
- package/addon/routes/pods/explorer/content.js +0 -10
- package/addon/routes/pods/explorer.js +0 -44
- package/addon/routes/pods/index/pod.js +0 -3
- package/addon/routes/pods/index.js +0 -21
- package/addon/templates/pods/explorer/content.hbs +0 -19
- package/addon/templates/pods/index/pod.hbs +0 -11
- package/addon/templates/pods/index.hbs +0 -19
- package/server/src/LegacyClient/Identity/IdentityProvider.php +0 -174
- package/server/src/LegacyClient/Identity/Profile.php +0 -18
- package/server/src/LegacyClient/OIDCClient.php +0 -350
- package/server/src/LegacyClient/Profile/WebID.php +0 -26
- package/server/src/LegacyClient/SolidClient.php +0 -271
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace Fleetbase\Solid\LegacyClient;
|
|
4
|
-
|
|
5
|
-
use Fleetbase\Support\Utils;
|
|
6
|
-
use Illuminate\Support\Facades\Redis;
|
|
7
|
-
use IlluminateAgnostic\Str\Support\Str;
|
|
8
|
-
use Jose\Component\Checker\AlgorithmChecker;
|
|
9
|
-
use Jose\Component\Checker\HeaderCheckerManager;
|
|
10
|
-
use Jose\Component\Core\AlgorithmManager;
|
|
11
|
-
use Jose\Component\Core\JWK;
|
|
12
|
-
use Jose\Component\Core\JWKSet;
|
|
13
|
-
use Jose\Component\KeyManagement\JWKFactory;
|
|
14
|
-
use Jose\Component\Signature\Algorithm\ES256;
|
|
15
|
-
use Jose\Component\Signature\Algorithm\ES384;
|
|
16
|
-
use Jose\Component\Signature\Algorithm\ES512;
|
|
17
|
-
use Jose\Component\Signature\Algorithm\HS256;
|
|
18
|
-
use Jose\Component\Signature\Algorithm\HS384;
|
|
19
|
-
use Jose\Component\Signature\Algorithm\HS512;
|
|
20
|
-
use Jose\Component\Signature\Algorithm\RS256;
|
|
21
|
-
use Jose\Component\Signature\Algorithm\RS384;
|
|
22
|
-
use Jose\Component\Signature\Algorithm\RS512;
|
|
23
|
-
use Jose\Component\Signature\JWS;
|
|
24
|
-
use Jose\Component\Signature\JWSBuilder;
|
|
25
|
-
use Jose\Component\Signature\JWSTokenSupport;
|
|
26
|
-
use Jose\Component\Signature\JWSVerifier;
|
|
27
|
-
use Jose\Component\Signature\Serializer\CompactSerializer;
|
|
28
|
-
use Jose\Component\Signature\Serializer\JWSSerializerManager;
|
|
29
|
-
use Jumbojett\OpenIDConnectClient;
|
|
30
|
-
use Jumbojett\OpenIDConnectClientException;
|
|
31
|
-
|
|
32
|
-
final class OIDCClient extends OpenIDConnectClient
|
|
33
|
-
{
|
|
34
|
-
public const CLIENT_NAME = 'Fleetbase';
|
|
35
|
-
private ?JWK $dpopPrivateKey = null;
|
|
36
|
-
private ?string $redirectURL = null;
|
|
37
|
-
protected $refreshToken;
|
|
38
|
-
protected $idToken;
|
|
39
|
-
protected $tokenResponse;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @param $provider_url string optional
|
|
43
|
-
* @param $client_id string optional
|
|
44
|
-
* @param $client_secret string optional
|
|
45
|
-
* @param null $issuer
|
|
46
|
-
*/
|
|
47
|
-
public function __construct($provider_url = null, $client_id = null, $client_secret = null, $issuer = null)
|
|
48
|
-
{
|
|
49
|
-
$this->setProviderURL($provider_url);
|
|
50
|
-
if ($issuer === null) {
|
|
51
|
-
$this->setIssuer($provider_url);
|
|
52
|
-
} else {
|
|
53
|
-
$this->setIssuer($issuer);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// $this->setDefaultRedirectURL();
|
|
57
|
-
$this->setCodeChallengeMethod('S256');
|
|
58
|
-
$this->setClientID($client_id);
|
|
59
|
-
$this->setClientSecret($client_secret);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public static function create(SolidClient $solidClient): self
|
|
63
|
-
{
|
|
64
|
-
$oidcConfig = $solidClient->getOpenIdConfiguration();
|
|
65
|
-
$oidcClient = new self(data_get($oidcConfig, 'issuer'));
|
|
66
|
-
$oidcClient->providerConfigParam($oidcConfig);
|
|
67
|
-
|
|
68
|
-
return $oidcClient;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
public function authenticate(): bool
|
|
72
|
-
{
|
|
73
|
-
$this->setCodeChallengeMethod('S256');
|
|
74
|
-
$this->addScope(['openid', 'webid', 'offline_access']);
|
|
75
|
-
|
|
76
|
-
return parent::authenticate();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public function setClientCredentials(string $clientName = self::CLIENT_NAME, $clientCredentials): ?self
|
|
80
|
-
{
|
|
81
|
-
$clientId = data_get($clientCredentials, 'client_id');
|
|
82
|
-
$clientSecret = data_get($clientCredentials, 'client_secret');
|
|
83
|
-
|
|
84
|
-
$this->setClientName($clientName);
|
|
85
|
-
$this->setClientID($clientId);
|
|
86
|
-
$this->setClientSecret($clientSecret);
|
|
87
|
-
$this->storeClientCredentials($clientName, $clientCredentials);
|
|
88
|
-
|
|
89
|
-
return $this;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
private function storeClientCredentials(?string $clientName = self::CLIENT_NAME, $clientCredentials): void
|
|
93
|
-
{
|
|
94
|
-
Redis::set('oidc:client:' . Str::slug($clientName), json_encode($clientCredentials));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
public function getClientCredentials(string $clientName = self::CLIENT_NAME)
|
|
98
|
-
{
|
|
99
|
-
$clientCredentialsString = Redis::get('oidc:client:' . Str::slug($clientName));
|
|
100
|
-
if (Utils::isJson($clientCredentialsString)) {
|
|
101
|
-
return json_decode($clientCredentialsString, false);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
public function restoreClientCredentials(string $clientName = self::CLIENT_NAME): ?self
|
|
108
|
-
{
|
|
109
|
-
$clientCredentials = $this->getClientCredentials($clientName);
|
|
110
|
-
if ($clientCredentials) {
|
|
111
|
-
return $this->setClientCredentials($clientName, $clientCredentials);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
public function verifyJWTsignature($jwt): bool
|
|
118
|
-
{
|
|
119
|
-
$this->decodeToken($jwt);
|
|
120
|
-
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
public function requestTokens($code, $headers = [])
|
|
125
|
-
{
|
|
126
|
-
$headers[] = 'DPoP: ' . $this->createDPoP('POST', $this->getProviderConfigValue('token_endpoint'), false);
|
|
127
|
-
|
|
128
|
-
return parent::requestTokens($code, $headers);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// https://base64.guru/developers/php/examples/base64url
|
|
132
|
-
private function base64urlEncode(string $data): string
|
|
133
|
-
{
|
|
134
|
-
// First of all you should encode $data to Base64 string
|
|
135
|
-
$b64 = base64_encode($data);
|
|
136
|
-
|
|
137
|
-
// Convert Base64 to Base64URL by replacing “+” with “-” and “/” with “_”
|
|
138
|
-
$url = strtr($b64, '+/', '-_');
|
|
139
|
-
|
|
140
|
-
// Remove padding character from the end of line and return the Base64URL result
|
|
141
|
-
return rtrim($url, '=');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
public function createDPoP(string $method, string $url, bool $includeAth = true, ?string $accessToken = null): string
|
|
145
|
-
{
|
|
146
|
-
if (null === $this->dpopPrivateKey) {
|
|
147
|
-
$this->dpopPrivateKey = JWKFactory::createECKey('P-256', ['use' => 'sig', 'kid' => base64_encode(random_bytes(20))]);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
$jwsBuilder = new JWSBuilder(new AlgorithmManager([new ES256()]));
|
|
151
|
-
|
|
152
|
-
$arrayPayload = [
|
|
153
|
-
'htu' => strtok($url, '?'),
|
|
154
|
-
'htm' => $method,
|
|
155
|
-
'jti' => base64_encode(random_bytes(20)),
|
|
156
|
-
'iat' => time(),
|
|
157
|
-
];
|
|
158
|
-
if ($includeAth) {
|
|
159
|
-
if (!$accessToken) {
|
|
160
|
-
$accessToken = $this->getAccessToken();
|
|
161
|
-
}
|
|
162
|
-
$arrayPayload['ath'] = $this->base64urlEncode(hash('sha256', $accessToken));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
$payload = json_encode($arrayPayload, \JSON_THROW_ON_ERROR);
|
|
166
|
-
|
|
167
|
-
$jws = $jwsBuilder
|
|
168
|
-
->create()
|
|
169
|
-
->withPayload($payload)
|
|
170
|
-
->addSignature(
|
|
171
|
-
$this->dpopPrivateKey,
|
|
172
|
-
[
|
|
173
|
-
'alg' => 'ES256',
|
|
174
|
-
'typ' => 'dpop+jwt',
|
|
175
|
-
'jwk' => $this->dpopPrivateKey->toPublic()->jsonSerialize(),
|
|
176
|
-
]
|
|
177
|
-
)
|
|
178
|
-
->build();
|
|
179
|
-
|
|
180
|
-
return (new CompactSerializer())->serialize($jws, 0);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
public function decodeToken(string $jwt): JWS
|
|
184
|
-
{
|
|
185
|
-
try {
|
|
186
|
-
$jwks = JWKSet::createFromJson($this->fetchURL($this->getProviderConfigValue('jwks_uri')));
|
|
187
|
-
} catch (\Exception $e) {
|
|
188
|
-
throw new OpenIDConnectClientException('Invalid JWKS: ' . $e->getMessage(), $e->getCode(), $e);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
$headerCheckerManager = new HeaderCheckerManager(
|
|
192
|
-
[new AlgorithmChecker(['RS256', 'RS384', 'R512', 'HS256', 'HS384', 'HS512', 'ES256', 'ES384', 'ES512'])], // TODO: read this from the provider config
|
|
193
|
-
[new JWSTokenSupport()],
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
$algorithmManager = new AlgorithmManager([
|
|
197
|
-
new RS256(),
|
|
198
|
-
new RS384(),
|
|
199
|
-
new RS512(),
|
|
200
|
-
new HS256(),
|
|
201
|
-
new HS384(),
|
|
202
|
-
new HS512(),
|
|
203
|
-
new ES256(),
|
|
204
|
-
new ES384(),
|
|
205
|
-
new ES512(),
|
|
206
|
-
]);
|
|
207
|
-
|
|
208
|
-
$serializerManager = new JWSSerializerManager([new CompactSerializer()]);
|
|
209
|
-
$jws = $serializerManager->unserialize($jwt);
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
$headerCheckerManager->check($jws, 0);
|
|
213
|
-
} catch (\Exception $e) {
|
|
214
|
-
throw new OpenIDConnectClientException('Invalid JWT header: ' . $e->getMessage(), $e->getCode(), $e);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
$jwsVerifier = new JWSVerifier($algorithmManager);
|
|
218
|
-
if (!$jwsVerifier->verifyWithKeySet($jws, $jwks, 0)) {
|
|
219
|
-
throw new OpenIDConnectClientException('Invalid JWT signature.');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return $jws;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
public function getRedirectURL()
|
|
226
|
-
{
|
|
227
|
-
// If the redirect URL has been set then return it.
|
|
228
|
-
if (property_exists($this, 'redirectURL') && $this->redirectURL) {
|
|
229
|
-
return $this->redirectURL;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// // Get current solid identity
|
|
233
|
-
// $solidIdentity = SolidIdentity::current();
|
|
234
|
-
// return Utils::apiUrl('solid/int/v1/oidc/complete-registration/' . $solidIdentity->request_code, [], 8000);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
public function setDefaultRedirectURL(string $redirectURL)
|
|
238
|
-
{
|
|
239
|
-
$this->setRedirectURL($redirectURL);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
public function setAccessToken($accessToken)
|
|
243
|
-
{
|
|
244
|
-
$this->accessToken = $accessToken;
|
|
245
|
-
Redis::set('oidc:client:access_token', $accessToken);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
public function setIdToken($idToken)
|
|
249
|
-
{
|
|
250
|
-
$this->idToken = $idToken;
|
|
251
|
-
Redis::set('oidc:client:id_token', $idToken);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
public function setRefreshToken($refreshToken)
|
|
255
|
-
{
|
|
256
|
-
$this->refreshToken = $refreshToken;
|
|
257
|
-
Redis::set('oidc:client:refresh_token', $refreshToken);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
public function setTokenResponse($tokenResponse)
|
|
261
|
-
{
|
|
262
|
-
$this->tokenResponse = $tokenResponse;
|
|
263
|
-
Redis::set('oidc:client:token_response', json_encode($tokenResponse));
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
public function storeTokens()
|
|
267
|
-
{
|
|
268
|
-
$idToken = $this->getIdToken();
|
|
269
|
-
if ($idToken) {
|
|
270
|
-
Redis::set('oidc:client:id_token', $idToken);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
$accessToken = $this->getAccessToken();
|
|
274
|
-
if ($accessToken) {
|
|
275
|
-
Redis::set('oidc:client:access_token', $accessToken);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
$refreshToken = $this->getRefreshToken();
|
|
279
|
-
if ($refreshToken) {
|
|
280
|
-
Redis::set('oidc:client:refresh_token', $refreshToken);
|
|
281
|
-
}
|
|
282
|
-
$tokenResponse = $this->getTokenResponse();
|
|
283
|
-
if ($tokenResponse) {
|
|
284
|
-
Redis::set('oidc:client:token_response', json_encode($tokenResponse));
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return $this;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
public function getAccessToken(): ?string
|
|
291
|
-
{
|
|
292
|
-
$accessToken = parent::getAccessToken();
|
|
293
|
-
if (!$accessToken) {
|
|
294
|
-
$accessToken = Redis::get('oidc:client:access_token');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return $accessToken;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
public function getRefreshToken(): ?string
|
|
301
|
-
{
|
|
302
|
-
$refreshToken = parent::getRefreshToken();
|
|
303
|
-
if (!$refreshToken) {
|
|
304
|
-
$refreshToken = Redis::get('oidc:client:refresh_token');
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return $refreshToken;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
public function getIdToken()
|
|
311
|
-
{
|
|
312
|
-
$idToken = parent::getIdToken();
|
|
313
|
-
if (!$idToken) {
|
|
314
|
-
$idToken = Redis::get('oidc:client:id_token');
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return $idToken;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
public function getTokenResponse()
|
|
321
|
-
{
|
|
322
|
-
$tokenResponse = parent::getTokenResponse();
|
|
323
|
-
if (!$tokenResponse) {
|
|
324
|
-
$tokenResponse = Redis::get('oidc:client:token_response');
|
|
325
|
-
if (is_string($tokenResponse)) {
|
|
326
|
-
$tokenResponse = json_decode($tokenResponse);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return $tokenResponse;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
public function restoreTokens()
|
|
334
|
-
{
|
|
335
|
-
$idToken = Redis::get('oidc:client:id_token');
|
|
336
|
-
$accessToken = Redis::get('oidc:client:access_token');
|
|
337
|
-
$refreshToken = Redis::get('oidc:client:refresh_token');
|
|
338
|
-
$tokenResponse = Redis::get('oidc:client:token_response');
|
|
339
|
-
|
|
340
|
-
$this->setIdToken($idToken);
|
|
341
|
-
$this->setAccessToken($accessToken);
|
|
342
|
-
$this->setRefreshToken($refreshToken);
|
|
343
|
-
$this->setTokenResponse($tokenResponse);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
public function clearStoredClient(?string $clientName = self::CLIENT_NAME)
|
|
347
|
-
{
|
|
348
|
-
Redis::del('oidc:client:' . Str::slug($clientName));
|
|
349
|
-
}
|
|
350
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace Fleetbase\Solid\LegacyClient\Profile;
|
|
4
|
-
|
|
5
|
-
class WebID
|
|
6
|
-
{
|
|
7
|
-
protected function getAltProfileUrlAllFrom(string $webId, string $webIdProfile): array
|
|
8
|
-
{
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
protected function getProfileAll(string $webId, array $options = [])
|
|
12
|
-
{
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
protected function getPodUrlAll(string $webId, array $options = [])
|
|
16
|
-
{
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
protected function getPodUrlAllFrom(array $profiles, string $webId)
|
|
20
|
-
{
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
protected function getWebIdDataset(string $webId)
|
|
24
|
-
{
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace Fleetbase\Solid\LegacyClient;
|
|
4
|
-
|
|
5
|
-
use EasyRdf\Graph;
|
|
6
|
-
use Fleetbase\Solid\Client\Identity\IdentityProvider;
|
|
7
|
-
use Fleetbase\Solid\Models\SolidIdentity;
|
|
8
|
-
use Illuminate\Http\Client\Response;
|
|
9
|
-
use Illuminate\Support\Facades\Http;
|
|
10
|
-
use Illuminate\Support\Str;
|
|
11
|
-
|
|
12
|
-
class SolidClient
|
|
13
|
-
{
|
|
14
|
-
/**
|
|
15
|
-
* The host of the Solid server.
|
|
16
|
-
*/
|
|
17
|
-
private string $host = 'localhost';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The port on which the Solid server is running.
|
|
21
|
-
*/
|
|
22
|
-
private int $port = 3000;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Indicates whether the connection to the Solid server should be secure (HTTPS).
|
|
26
|
-
*/
|
|
27
|
-
private bool $secure = true;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The identity provider for authentication with the Solid server.
|
|
31
|
-
*/
|
|
32
|
-
public IdentityProvider $identity;
|
|
33
|
-
|
|
34
|
-
public bool $identityProviderInitialized = false;
|
|
35
|
-
|
|
36
|
-
private const DEFAULT_MIME_TYPE = 'text/turtle';
|
|
37
|
-
private const LDP_BASIC_CONTAINER = 'http://www.w3.org/ns/ldp#BasicContainer';
|
|
38
|
-
private const LDP_RESOURCE = 'http://www.w3.org/ns/ldp#Resource';
|
|
39
|
-
private const OIDC_ISSUER = 'http://www.w3.org/ns/solid/terms#oidcIssuer';
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Constructor for the SolidClient.
|
|
43
|
-
*
|
|
44
|
-
* Initializes the client with the provided options or defaults.
|
|
45
|
-
*
|
|
46
|
-
* @param array $options configuration options for the client
|
|
47
|
-
*/
|
|
48
|
-
public function __construct(array $options = [])
|
|
49
|
-
{
|
|
50
|
-
$this->host = config('solid.server.host', data_get($options, 'host'));
|
|
51
|
-
$this->port = (int) config('solid.server.port', data_get($options, 'port'));
|
|
52
|
-
$this->secure = (bool) config('solid.server.secure', data_get($options, 'secure'));
|
|
53
|
-
$this->initializeIdentityProvider($options);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private function initializeIdentityProvider(array $options = []): IdentityProvider
|
|
57
|
-
{
|
|
58
|
-
$this->identity = new IdentityProvider($this);
|
|
59
|
-
if (isset($options['restore']) && is_string($options['restore'])) {
|
|
60
|
-
$this->identity->restoreClientCredentials($options['restore']);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return $this->identity;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Factory method to create a new instance of the SolidClient.
|
|
68
|
-
*
|
|
69
|
-
* @param array $options configuration options for the client
|
|
70
|
-
*
|
|
71
|
-
* @return static a new instance of SolidClient
|
|
72
|
-
*/
|
|
73
|
-
public static function create(array $options = []): self
|
|
74
|
-
{
|
|
75
|
-
return new static($options);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Constructs the URL to the Solid server based on the configured host, port, and security.
|
|
80
|
-
*
|
|
81
|
-
* @return string the fully constructed URL
|
|
82
|
-
*/
|
|
83
|
-
public function getServerUrl(): string
|
|
84
|
-
{
|
|
85
|
-
$protocol = $this->secure ? 'https' : 'http';
|
|
86
|
-
$host = preg_replace('#^.*://#', '', $this->host);
|
|
87
|
-
|
|
88
|
-
return "{$protocol}://{$host}:{$this->port}";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Creates a full request URL based on the server URL and the provided URI.
|
|
93
|
-
*
|
|
94
|
-
* This function constructs a complete URL by appending the given URI to the base server URL.
|
|
95
|
-
* It ensures that there is exactly one slash between the base URL and the URI.
|
|
96
|
-
*
|
|
97
|
-
* @param string|null $uri The URI to append to the server URL. If null, only the server URL is returned.
|
|
98
|
-
*
|
|
99
|
-
* @return string the fully constructed URL
|
|
100
|
-
*/
|
|
101
|
-
private function createRequestUrl(?string $uri = null): string
|
|
102
|
-
{
|
|
103
|
-
$url = $this->getServerUrl();
|
|
104
|
-
|
|
105
|
-
if (is_string($uri)) {
|
|
106
|
-
$uri = '/' . ltrim($uri, '/');
|
|
107
|
-
$url .= $uri;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return $url;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Sets the necessary authentication headers for the request.
|
|
115
|
-
*
|
|
116
|
-
* This function adds authentication headers to the provided options array.
|
|
117
|
-
* It includes an Authorization header with a DPoP token if an access token is available.
|
|
118
|
-
* It also generates a DPoP header based on the request method and URL.
|
|
119
|
-
*
|
|
120
|
-
* @param array &$options The array of options for the HTTP request, passed by reference
|
|
121
|
-
* @param string $method The HTTP method of the request (e.g., 'GET', 'POST').
|
|
122
|
-
* @param string $url the full URL of the request
|
|
123
|
-
*
|
|
124
|
-
* @return array the modified options array with added authentication headers
|
|
125
|
-
*/
|
|
126
|
-
private function setAuthenticationHeaders(array &$options, string $method, string $url)
|
|
127
|
-
{
|
|
128
|
-
$withoutAuth = data_get($options, 'withoutAuth', false);
|
|
129
|
-
if ($withoutAuth) {
|
|
130
|
-
return $options;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
$useCssAuth = data_get($options, 'useCssAuth', false);
|
|
134
|
-
$useDpopAuth = data_get($options, 'useDpopAuth', true);
|
|
135
|
-
$headers = data_get($options, 'headers', []);
|
|
136
|
-
$accessToken = isset($this->identity) ? $this->identity->getAccessToken() : null;
|
|
137
|
-
if ($accessToken) {
|
|
138
|
-
if ($useDpopAuth) {
|
|
139
|
-
$headers['Authorization'] = 'DPoP ' . $accessToken;
|
|
140
|
-
$headers['DPoP'] = $this->identity->createDPoP($method, $url, true);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if ($useCssAuth) {
|
|
144
|
-
$headers['Authorization'] = 'CSS-Account-Token ' . $accessToken;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
$options['headers'] = $headers;
|
|
149
|
-
|
|
150
|
-
return $options;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Make a HTTP request to the Solid server.
|
|
155
|
-
*
|
|
156
|
-
* @param string $method The HTTP method (GET, POST, etc.)
|
|
157
|
-
* @param string $uri The URI to send the request to
|
|
158
|
-
* @param array $options Options for the request
|
|
159
|
-
*/
|
|
160
|
-
protected function request(string $method, string $uri, array $data = [], array $options = []): Response
|
|
161
|
-
{
|
|
162
|
-
if (Str::startsWith($uri, 'http')) {
|
|
163
|
-
$url = $uri;
|
|
164
|
-
} else {
|
|
165
|
-
$url = $this->createRequestUrl($uri);
|
|
166
|
-
}
|
|
167
|
-
$this->setAuthenticationHeaders($options, $method, $url);
|
|
168
|
-
|
|
169
|
-
return Http::withOptions($options)->{$method}($url, $data);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
public function requestWithIdentity(SolidIdentity $solidIdentity, string $method, string $uri, array $data = [], array $options = [])
|
|
173
|
-
{
|
|
174
|
-
if (Str::startsWith($uri, 'http')) {
|
|
175
|
-
$url = $uri;
|
|
176
|
-
} else {
|
|
177
|
-
$url = $this->createRequestUrl($uri);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// prepare headers
|
|
181
|
-
if (!isset($options['headers']) || !is_array($options['headers'])) {
|
|
182
|
-
$options['headers'] = [];
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
$accessToken = $solidIdentity->getAccessToken();
|
|
186
|
-
if ($accessToken) {
|
|
187
|
-
$options['headers']['Authorization'] = 'DPoP ' . $accessToken;
|
|
188
|
-
$options['headers']['DPoP'] = $this->identity->createDPoP($method, $url, true, $accessToken);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// dump($options);
|
|
192
|
-
return Http::withOptions($options)->{$method}($url, $data);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Send a GET request to the Solid server.
|
|
197
|
-
*
|
|
198
|
-
* @param string $uri The URI to send the request to
|
|
199
|
-
* @param array $options Options for the request
|
|
200
|
-
*/
|
|
201
|
-
public function get(string $uri, array $data = [], array $options = []): Response
|
|
202
|
-
{
|
|
203
|
-
return $this->request('get', $uri, $data, $options);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Send a POST request to the Solid server.
|
|
208
|
-
*
|
|
209
|
-
* @param string $uri The URI to send the request to
|
|
210
|
-
* @param array $data Data to be sent in the request body
|
|
211
|
-
*/
|
|
212
|
-
public function post(string $uri, array $data = [], array $options = []): Response
|
|
213
|
-
{
|
|
214
|
-
return $this->request('post', $uri, $data, $options);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Send a PUT request to the Solid server.
|
|
219
|
-
*
|
|
220
|
-
* @param string $uri The URI to send the request to
|
|
221
|
-
* @param array $data Data to be sent in the request body
|
|
222
|
-
*/
|
|
223
|
-
public function put(string $uri, array $data = [], array $options = []): Response
|
|
224
|
-
{
|
|
225
|
-
return $this->request('put', $uri, $data, $options);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Send a PATCH request to the Solid server.
|
|
230
|
-
*
|
|
231
|
-
* @param string $uri The URI to send the request to
|
|
232
|
-
* @param array $data Data to be sent in the request body
|
|
233
|
-
*/
|
|
234
|
-
public function patch(string $uri, array $data = [], array $options = []): Response
|
|
235
|
-
{
|
|
236
|
-
return $this->request('patch', $uri, $data, $options);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Send a DELETE request to the Solid server.
|
|
241
|
-
*
|
|
242
|
-
* @param string $uri The URI to send the request to
|
|
243
|
-
* @param array $options Options for the request
|
|
244
|
-
*/
|
|
245
|
-
public function delete(string $uri, array $data = [], array $options = []): Response
|
|
246
|
-
{
|
|
247
|
-
return $this->request('delete', $uri, $data, $options);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
public function getProfile(string $webId, array $options = []): Graph
|
|
251
|
-
{
|
|
252
|
-
$response = $this->get($webId, $options);
|
|
253
|
-
if (null !== $format = $response->getHeaders()['content-type'][0] ?? null) {
|
|
254
|
-
// strip parameters (such as charset) if any
|
|
255
|
-
$format = explode(';', $format, 2)[0];
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return new Graph($webId, $response->getContent(), $format);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
public function getOpenIdConfiguration()
|
|
262
|
-
{
|
|
263
|
-
$response = $this->get('.well-known/openid-configuration', [], ['withoutAuth' => true]);
|
|
264
|
-
|
|
265
|
-
if ($response->successful()) {
|
|
266
|
-
return $response->json();
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
throw $response->toException();
|
|
270
|
-
}
|
|
271
|
-
}
|