Skip to content

Commit 713ff79

Browse files
committed
storage server
1 parent 2620d17 commit 713ff79

2 files changed

Lines changed: 341 additions & 0 deletions

File tree

lib/StorageServer.php

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
<?php
2+
namespace Pdsinterop\PhpSolid;
3+
4+
use Pdsinterop\PhpSolid\Server;
5+
use Pdsinterop\PhpSolid\User;
6+
7+
class StorageServer extends Server {
8+
public static function getFileSystem() {
9+
$storageId = self::getStorageId();
10+
11+
// The internal adapter
12+
$adapter = new \League\Flysystem\Adapter\Local(
13+
// Determine root directory
14+
STORAGEBASE . "$storageId/"
15+
);
16+
17+
$graph = new \EasyRdf\Graph();
18+
// Create Formats objects
19+
$formats = new \Pdsinterop\Rdf\Formats();
20+
21+
$scheme = $_SERVER['REQUEST_SCHEME'];
22+
$domain = $_SERVER['SERVER_NAME'];
23+
$path = $_SERVER['REQUEST_URI'];
24+
$serverUri = "{$scheme}://{$domain}{$path}"; // FIXME: doublecheck that this is the correct url;
25+
26+
// Create the RDF Adapter
27+
$rdfAdapter = new \Pdsinterop\Rdf\Flysystem\Adapter\Rdf($adapter, $graph, $formats, $serverUri);
28+
29+
$filesystem = new \League\Flysystem\Filesystem($rdfAdapter);
30+
$filesystem->addPlugin(new \Pdsinterop\Rdf\Flysystem\Plugin\AsMime($formats));
31+
$plugin = new \Pdsinterop\Rdf\Flysystem\Plugin\ReadRdf($graph);
32+
$filesystem->addPlugin($plugin);
33+
return $filesystem;
34+
}
35+
36+
public static function respond($response) {
37+
$statusCode = $response->getStatusCode();
38+
$response->getBody()->rewind();
39+
$headers = $response->getHeaders();
40+
41+
$body = $response->getBody()->getContents();
42+
header("HTTP/1.1 $statusCode");
43+
foreach ($headers as $header => $values) {
44+
foreach ($values as $value) {
45+
if ($header == "Location") {
46+
$value = preg_replace("|%26%2334%3B|", "%22", $value); // odoo weird encoding
47+
}
48+
header($header . ":" . $value);
49+
}
50+
}
51+
echo $body;
52+
}
53+
54+
public static function getWebId($rawRequest) {
55+
$dpop = self::getDpop();
56+
$webId = $dpop->getWebId($rawRequest);
57+
if (!isset($webId)) {
58+
$bearer = self::getBearer();
59+
$webId = $bearer->getWebId($rawRequest);
60+
}
61+
return $webId;
62+
}
63+
64+
private static function getStorageId() {
65+
$serverName = $_SERVER['SERVER_NAME'];
66+
$idParts = explode(".", $serverName, 2);
67+
$storageId = preg_replace("/^storage-/", "", $idParts[0]);
68+
return $storageId;
69+
}
70+
71+
public static function getOwner() {
72+
$storageId = self::getStorageId();
73+
return User::getUserById($storageId);
74+
}
75+
76+
public static function getOwnerWebId() {
77+
$owner = self::getOwner();
78+
return $owner['webId'];
79+
}
80+
81+
public static function initializeStorage() {
82+
$filesystem = self::getFilesystem();
83+
if (!$filesystem->has("/.acl")) {
84+
$defaultAcl = self::generateDefaultAcl();
85+
$filesystem->write("/.acl", $defaultAcl);
86+
}
87+
88+
// Generate default folders and ACLs:
89+
if (!$filesystem->has("/inbox")) {
90+
$filesystem->createDir("/inbox");
91+
}
92+
if (!$filesystem->has("/inbox/.acl")) {
93+
$inboxAcl = self::generatePublicAppendAcl();
94+
$filesystem->write("/inbox/.acl", $inboxAcl);
95+
}
96+
if (!$filesystem->has("/settings")) {
97+
$filesystem->createDir("/settings");
98+
}
99+
if (!$filesystem->has("/settings/privateTypeIndex.ttl")) {
100+
$privateTypeIndex = self::generateDefaultPrivateTypeIndex();
101+
$filesystem->write("/settings/privateTypeIndex.ttl", $privateTypeIndex);
102+
}
103+
if (!$filesystem->has("/settings/publicTypeIndex.ttl")) {
104+
$publicTypeIndex = self::generateDefaultPublicTypeIndex();
105+
$filesystem->write("/settings/publicTypeIndex.ttl", $publicTypeIndex);
106+
}
107+
if (!$filesystem->has("/settings/preferences.ttl")) {
108+
$preferences = self::generateDefaultPreferences();
109+
$filesystem->write("/settings/preferences.ttl", $preferences);
110+
}
111+
if (!$filesystem->has("/public")) {
112+
$filesystem->createDir("/public");
113+
}
114+
if (!$filesystem->has("/public/.acl")) {
115+
$publicAcl = self::generatePublicReadAcl();
116+
$filesystem->write("/public/.acl", $publicAcl);
117+
}
118+
if (!$filesystem->has("/private")) {
119+
$filesystem->createDir("/private");
120+
}
121+
}
122+
123+
public static function generateDefaultAcl() {
124+
$webId = self::getOwnerWebId();
125+
$acl = <<< "EOF"
126+
# Root ACL resource for the user account
127+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
128+
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
129+
130+
# The homepage is readable by the public
131+
<#public>
132+
a acl:Authorization;
133+
acl:agentClass foaf:Agent;
134+
acl:accessTo <./>;
135+
acl:mode acl:Read.
136+
137+
# The owner has full access to every resource in their pod.
138+
# Other agents have no access rights,
139+
# unless specifically authorized in other .acl resources.
140+
<#owner>
141+
a acl:Authorization;
142+
acl:agent <$webId>;
143+
# Set the access to the root storage folder itself
144+
acl:accessTo <./>;
145+
# All resources will inherit this authorization, by default
146+
acl:default <./>;
147+
# The owner has all of the access modes allowed
148+
acl:mode
149+
acl:Read, acl:Write, acl:Control.
150+
EOF;
151+
return $acl;
152+
}
153+
154+
public static function generatePublicAppendAcl() {
155+
$webId = self::getOwnerWebId();
156+
$acl = <<< "EOF"
157+
# Inbox ACL resource for the user account
158+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
159+
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
160+
161+
<#public>
162+
a acl:Authorization;
163+
acl:agentClass foaf:Agent;
164+
acl:accessTo <./>;
165+
acl:default <./>;
166+
acl:mode
167+
acl:Append.
168+
169+
<#owner>
170+
a acl:Authorization;
171+
acl:agent <$webId>;
172+
# Set the access to the root storage folder itself
173+
acl:accessTo <./>;
174+
# All resources will inherit this authorization, by default
175+
acl:default <./>;
176+
# The owner has all of the access modes allowed
177+
acl:mode
178+
acl:Read, acl:Write, acl:Control.
179+
EOF;
180+
return $acl;
181+
}
182+
183+
public static function generatePublicReadAcl() {
184+
$webId = self::getOwnerWebId();
185+
$acl = <<< "EOF"
186+
# Inbox ACL resource for the user account
187+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
188+
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
189+
190+
<#public>
191+
a acl:Authorization;
192+
acl:agentClass foaf:Agent;
193+
acl:accessTo <./>;
194+
acl:default <./>;
195+
acl:mode
196+
acl:Read.
197+
198+
<#owner>
199+
a acl:Authorization;
200+
acl:agent <$webId>;
201+
# Set the access to the root storage folder itself
202+
acl:accessTo <./>;
203+
# All resources will inherit this authorization, by default
204+
acl:default <./>;
205+
# The owner has all of the access modes allowed
206+
acl:mode
207+
acl:Read, acl:Write, acl:Control.
208+
EOF;
209+
return $acl;
210+
}
211+
212+
public static function generateDefaultPrivateTypeIndex() {
213+
$typeIndex = <<< "EOF"
214+
# Private type index
215+
@prefix : <#>.
216+
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
217+
218+
<>
219+
a solid:UnlistedDocument, solid:TypeIndex.
220+
EOF;
221+
return $typeIndex;
222+
}
223+
224+
public static function generateDefaultPublicTypeIndex() {
225+
$typeIndex = <<< "EOF"
226+
# Public type index
227+
@prefix : <#>.
228+
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
229+
230+
<>
231+
a solid:ListedDocument, solid:TypeIndex.
232+
EOF;
233+
return $typeIndex;
234+
}
235+
236+
public static function generateDefaultPreferences() {
237+
$webId = self::getOwnerWebId();
238+
$preferences = <<< "EOF"
239+
# Preferences
240+
@prefix : <#>.
241+
@prefix sp: <http://www.w3.org/ns/pim/space#>.
242+
@prefix dct: <http://purl.org/dc/terms/>.
243+
@prefix profile: <$webId>.
244+
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
245+
246+
<>
247+
a sp:ConfigurationFile;
248+
dct:title "Preferences file".
249+
250+
profile:me
251+
a solid:Developer;
252+
solid:privateTypeIndex <privateTypeIndex.ttl>;
253+
solid:publicTypeIndex <publicTypeIndex.ttl>.
254+
EOF;
255+
return $preferences;
256+
}
257+
}
258+

www/profile/storage.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
require_once(__DIR__ . "/../../config.php");
3+
require_once(__DIR__ . "/../../vendor/autoload.php");
4+
5+
use Pdsinterop\PhpSolid\Middleware;
6+
use Pdsinterop\PhpSolid\StorageServer;
7+
use Pdsinterop\PhpSolid\ClientRegistration;
8+
use Pdsinterop\PhpSolid\SolidNotifications;
9+
use Pdsinterop\Solid\Auth\WAC;
10+
use Pdsinterop\Solid\Resources\Server as ResourceServer;
11+
use Laminas\Diactoros\ServerRequestFactory;
12+
use Laminas\Diactoros\Response;
13+
14+
$request = explode("?", $_SERVER['REQUEST_URI'], 2)[0];
15+
$method = $_SERVER['REQUEST_METHOD'];
16+
17+
Middleware::cors();
18+
19+
switch ($method) {
20+
case "OPTIONS":
21+
echo "OK";
22+
return;
23+
break;
24+
}
25+
26+
$requestFactory = new ServerRequestFactory();
27+
$rawRequest = $requestFactory->fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
28+
$response = new Response();
29+
30+
StorageServer::initializeStorage();
31+
$filesystem = StorageServer::getFileSystem();
32+
33+
$resourceServer = new ResourceServer($filesystem, $response, null);
34+
$solidNotifications = new SolidNotifications();
35+
$resourceServer->setNotifications($solidNotifications);
36+
37+
$wac = new WAC($filesystem);
38+
39+
$baseUrl = $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['SERVER_NAME'] . "/";
40+
41+
$resourceServer->setBaseUrl($baseUrl);
42+
$wac->setBaseUrl($baseUrl);
43+
44+
$webId = StorageServer::getWebId($rawRequest);
45+
46+
if (!isset($webId)) {
47+
$response = $resourceServer->getResponse()
48+
->withStatus(409, "Invalid token");
49+
StorageServer::respond($response);
50+
exit();
51+
}
52+
53+
$origin = $rawRequest->getHeaderLine("Origin");
54+
55+
// FIXME: Read allowed clients from the profile instead;
56+
$owner = StorageServer::getOwner();
57+
58+
$allowedClients = $owner['allowedClients'] ?? [];
59+
$allowedOrigins = [];
60+
foreach ($allowedClients as $clientId) {
61+
$clientRegistration = ClientRegistration::getRegistration($clientId);
62+
if (isset($clientRegistration['client_name'])) {
63+
$allowedOrigins[] = $clientRegistration['client_name'];
64+
}
65+
if (isset($clientRegistration['origin'])) {
66+
$allowedOrigins[] = $clientRegistration['origin'];
67+
}
68+
}
69+
if ($origin =="") {
70+
$allowedOrigins[] = "app://unset"; // FIXME: this should not be here.
71+
$origin = "app://unset";
72+
}
73+
74+
if (!$wac->isAllowed($rawRequest, $webId, $origin, $allowedOrigins)) {
75+
$response = new Response();
76+
$response = $response->withStatus(403, "Access denied!");
77+
StorageServer::respond($response);
78+
exit();
79+
}
80+
81+
$response = $resourceServer->respondToRequest($rawRequest);
82+
$response = $wac->addWACHeaders($rawRequest, $response, $webId);
83+
StorageServer::respond($response);

0 commit comments

Comments
 (0)