Skip to content

Commit e0de438

Browse files
authored
Merge pull request #22 from pdsinterop/feature/storage-owner
Feature/storage owner
2 parents be30983 + fbd1fe0 commit e0de438

18 files changed

Lines changed: 598 additions & 26 deletions

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
],
1717
"require": {
1818
"pdsinterop/solid-auth": "v0.13.0",
19-
"pdsinterop/solid-crud": "v0.8.2",
19+
"pdsinterop/solid-crud": "v0.8.3",
2020
"phpmailer/phpmailer": "^6.10",
2121
"sweetrdf/easyrdf": "~1.15.0",
2222
"phpseclib/bcmath_compat": "^2.0",

docker/solid.conf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,25 @@
1717
RewriteRule ^(.+)$ index.php [QSA,L]
1818
</Directory>
1919
</VirtualHost>
20+
<VirtualHost *:443>
21+
ServerName storage.solid.local
22+
DocumentRoot /opt/solid/www/storage
23+
24+
SSLEngine on
25+
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
26+
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
27+
28+
ErrorLog ${APACHE_LOG_DIR}/error.log
29+
CustomLog ${APACHE_LOG_DIR}/access.log combined
30+
<Directory />
31+
Require all granted
32+
RewriteEngine On
33+
RewriteBase /
34+
RewriteCond %{REQUEST_FILENAME} !-d
35+
RewriteCond %{REQUEST_FILENAME} !-f
36+
RewriteRule ^(.+)$ index.php [QSA,L]
37+
</Directory>
38+
</VirtualHost>
2039
<VirtualHost *:443>
2140
ServerName identity.solid.local
2241
ServerAlias *.solid.local

init.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,26 @@ function initDatabase() {
5858
echo $e->getMessage();
5959
}
6060
}
61-
61+
62+
function initStorageDatabase() {
63+
$statements = [
64+
'CREATE TABLE IF NOT EXISTS storage (
65+
storage_id VARCHAR(255) NOT NULL PRIMARY KEY,
66+
owner VARCHAR(255) NOT NULL
67+
)'
68+
];
69+
70+
try {
71+
$pdo = new \PDO("sqlite:" . DBPATH);
72+
73+
// create tables
74+
foreach($statements as $statement){
75+
$pdo->exec($statement);
76+
}
77+
} catch(\PDOException $e) {
78+
echo $e->getMessage();
79+
}
80+
}
6281
initKeys();
6382
initDatabase();
83+
initStorageDatabase();

lib/Routes/Account.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Pdsinterop\PhpSolid\Session;
88
use Pdsinterop\PhpSolid\Mailer;
99
use Pdsinterop\PhpSolid\IpAttempts;
10+
use Pdsinterop\PhpSolid\StorageServer;
1011

1112
class Account {
1213
public static function requireLoggedInUser() {
@@ -87,11 +88,15 @@ public static function respondToAccountNew() {
8788
header("HTTP/1.1 400 Bad Request");
8889
exit();
8990
}
91+
$createdStorage = StorageServer::createStorage($createdUser['webId']);
92+
9093
Mailer::sendAccountCreated($createdUser);
9194

9295
$responseData = array(
93-
"webId" => $createdUser['webId']
96+
"webId" => $createdUser['webId'],
97+
"storageUrl" => $createdStorage['storageUrl']
9498
);
99+
95100
header("HTTP/1.1 201 Created");
96101
header("Content-type: application/json");
97102
Session::start($_POST['email']);

lib/Routes/SolidStorage.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
namespace Pdsinterop\PhpSolid\Routes;
33

4+
use Pdsinterop\PhpSolid\User;
45
use Pdsinterop\PhpSolid\StorageServer;
56
use Pdsinterop\PhpSolid\ClientRegistration;
67
use Pdsinterop\PhpSolid\SolidNotifications;
@@ -15,8 +16,15 @@ public static function respondToStorage() {
1516
$requestFactory = new ServerRequestFactory();
1617
$rawRequest = $requestFactory->fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
1718

18-
StorageServer::initializeStorage();
19-
$filesystem = StorageServer::getFileSystem();
19+
try {
20+
StorageServer::initializeStorage();
21+
$filesystem = StorageServer::getFileSystem();
22+
} catch (\Exception $e) {
23+
$response = new Response();
24+
$response = $response->withStatus(404, "Not found");
25+
StorageServer::respond($response);
26+
exit();
27+
}
2028

2129
$resourceServer = new ResourceServer($filesystem, new Response(), null);
2230
$solidNotifications = new SolidNotifications();
@@ -40,9 +48,9 @@ public static function respondToStorage() {
4048
$origin = $rawRequest->getHeaderLine("Origin");
4149

4250
// FIXME: Read allowed clients from the profile instead;
43-
$owner = StorageServer::getOwner();
44-
45-
$allowedClients = $owner['allowedClients'] ?? [];
51+
// $owner = StorageServer::getOwner();
52+
$ownerWebId = StorageServer::getOwnerWebId();
53+
$owner = User::getUserByWebId($ownerWebId);
4654
$allowedOrigins = ($owner['allowedOrigins'] ?? []) + (TRUSTED_APPS ?? []);
4755

4856
if (!isset($origin) || ($origin === "")) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
namespace Pdsinterop\PhpSolid\Routes;
3+
4+
use Pdsinterop\PhpSolid\StorageServer;
5+
use Laminas\Diactoros\ServerRequestFactory;
6+
7+
class SolidStorageProvider {
8+
public static function respondToStorageNew() {
9+
$requestFactory = new ServerRequestFactory();
10+
$rawRequest = $requestFactory->fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
11+
$webId = StorageServer::getWebId($rawRequest);
12+
13+
if (!isset($webId) || $webId === "public") {
14+
header("HTTP/1.1 400 Bad Request");
15+
exit();
16+
}
17+
18+
// FIXME: Get the webID issuer and validate that we allow storage creation for that issuer
19+
20+
$createdStorage = StorageServer::createStorage($webId);
21+
if (!$createdStorage) {
22+
error_log("Failed to create storage");
23+
header("HTTP/1.1 400 Bad Request");
24+
exit();
25+
}
26+
27+
//Mailer::sendStorageCreated($createdStoage);
28+
29+
$storageUrl = "https://storage-" . $createdStorage['storageId'] . "." . BASEDOMAIN . "/";
30+
31+
$responseData = array(
32+
"storage" => $storageUrl
33+
);
34+
header("HTTP/1.1 201 Created");
35+
header("Content-type: application/json");
36+
echo json_encode($responseData, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
37+
}
38+
}
39+

lib/StorageServer.php

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,85 @@
44
use Pdsinterop\PhpSolid\Server;
55
use Pdsinterop\PhpSolid\User;
66
use Pdsinterop\PhpSolid\Util;
7+
use Pdsinterop\PhpSolid\Db;
78

89
class StorageServer extends Server {
9-
public static function getFileSystem() {
10+
public static function getStorage($storageId) {
11+
Db::connect();
12+
$query = Db::$pdo->prepare(
13+
'SELECT * FROM storage WHERE storage_id=:storageId'
14+
);
15+
$query->execute([
16+
':storageId' => $storageId
17+
]);
18+
return $query->fetchAll();
19+
}
20+
21+
public static function setStorageOwner($storageId, $owner) {
22+
Db::connect();
23+
$query = Db::$pdo->prepare(
24+
'UPDATE storage SET owner=:owner WHERE storage_id=:storageId'
25+
);
26+
$query->execute([
27+
':storageId' => $storageId,
28+
':owner' => $owner
29+
]);
30+
}
31+
32+
public static function createStorage($ownerWebId) {
33+
$generatedStorageId = bin2hex(random_bytes(16));
34+
while (self::storageIdExists($generatedStorageId)) {
35+
$generatedStorageId = bin2hex(random_bytes(16));
36+
}
37+
Db::connect();
38+
$query = Db::$pdo->prepare(
39+
'INSERT OR REPLACE INTO storage VALUES(:storageId, :owner)'
40+
);
41+
$query->execute([
42+
':storageId' => $generatedStorageId,
43+
':owner' => $ownerWebId
44+
]);
45+
return [
46+
"storageId" => $generatedStorageId
47+
];
48+
}
49+
50+
public static function storageIdExists($storageId) {
51+
Db::connect();
52+
$query = Db::$pdo->prepare(
53+
'SELECT storage_id FROM storage WHERE storage_id=:storageId'
54+
);
55+
$query->execute([
56+
':storageId' => $storageId
57+
]);
58+
$result = $query->fetchAll();
59+
if (sizeof($result) === 1) {
60+
return true;
61+
}
62+
return false;
63+
}
64+
65+
public static function getOwnerWebId() {
1066
$storageId = self::getStorageId();
67+
Db::connect();
68+
$query = Db::$pdo->prepare(
69+
'SELECT owner FROM storage WHERE storage_id=:storageId'
70+
);
71+
$query->execute([
72+
':storageId' => $storageId
73+
]);
74+
$result = $query->fetchAll();
75+
if (sizeof($result) === 1) {
76+
return $result[0]['owner'];
77+
}
78+
return false;
79+
}
1180

81+
public static function getFileSystem() {
82+
$storageId = self::getStorageId();
83+
if (!self::storageIdExists($storageId)) {
84+
throw new \Exception("Storage does not exist");
85+
}
1286
// The internal adapter
1387
$adapter = new \League\Flysystem\Adapter\Local(
1488
// Determine root directory
@@ -65,16 +139,6 @@ private static function getStorageId() {
65139
return $storageId;
66140
}
67141

68-
public static function getOwner() {
69-
$storageId = self::getStorageId();
70-
return User::getUserById($storageId);
71-
}
72-
73-
public static function getOwnerWebId() {
74-
$owner = self::getOwner();
75-
return $owner['webId'];
76-
}
77-
78142
public static function initializeStorage() {
79143
$filesystem = self::getFilesystem();
80144
if (!$filesystem->has("/.acl")) {
@@ -119,6 +183,9 @@ public static function initializeStorage() {
119183

120184
public static function generateDefaultAcl() {
121185
$webId = self::getOwnerWebId();
186+
if (!$webId) {
187+
throw new \Exception("No owner found for storage");
188+
}
122189
$acl = <<< "EOF"
123190
# Root ACL resource for the user account
124191
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@@ -150,6 +217,9 @@ public static function generateDefaultAcl() {
150217

151218
public static function generatePublicAppendAcl() {
152219
$webId = self::getOwnerWebId();
220+
if (!$webId) {
221+
throw new \Exception("No owner found for storage ID");
222+
}
153223
$acl = <<< "EOF"
154224
# Inbox ACL resource for the user account
155225
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@@ -179,6 +249,9 @@ public static function generatePublicAppendAcl() {
179249

180250
public static function generatePublicReadAcl() {
181251
$webId = self::getOwnerWebId();
252+
if (!$webId) {
253+
throw new \Exception("No owner found for storage ID");
254+
}
182255
$acl = <<< "EOF"
183256
# Inbox ACL resource for the user account
184257
@prefix acl: <http://www.w3.org/ns/auth/acl#>.

lib/User.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ public static function getUserById($userId) {
268268
return false;
269269
}
270270

271+
public static function getUserByWebId($webId) {
272+
$idParts = explode(".", $webId, 2);
273+
if ($idParts[1] !== BASEDOMAIN . "/#me") {
274+
return false;
275+
}
276+
$userId = preg_replace("/^id-/", "", $idParts[0]);
277+
return self::getUserById($userId);
278+
}
279+
271280
public static function checkPassword($email, $password) {
272281
Db::connect();
273282
$query = Db::$pdo->prepare(

tests/phpunit/StorageServerTest.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ class StorageServerTest extends \PHPUnit\Framework\TestCase
99
{
1010
public static $headers = [];
1111
public static $createdUser;
12+
public static $createdStorage;
1213

1314
protected function setUp(): void
1415
{
1516
$statements = [
1617
'DROP TABLE IF EXISTS allowedClients',
1718
'DROP TABLE IF EXISTS userStorage',
1819
'DROP TABLE IF EXISTS users',
20+
'DROP TABLE IF EXISTS storage',
1921
'CREATE TABLE IF NOT EXISTS allowedClients (
2022
userId VARCHAR(255) NOT NULL PRIMARY KEY,
2123
clientId VARCHAR(255) NOT NULL
@@ -30,6 +32,10 @@ protected function setUp(): void
3032
password TEXT NOT NULL,
3133
data TEXT
3234
)',
35+
'CREATE TABLE IF NOT EXISTS storage (
36+
storage_id VARCHAR(255) NOT NULL PRIMARY KEY,
37+
owner VARCHAR(255) NOT NULL
38+
)',
3339
];
3440

3541
Db::connect();
@@ -48,9 +54,11 @@ protected function setUp(): void
4854
"hello" => "world"
4955
];
5056
self::$createdUser = User::createUser($newUser);
57+
self::$createdStorage = StorageServer::createStorage(self::$createdUser['webId']);
58+
5159
$_SERVER['REQUEST_URI'] = "/test/";
5260
$_SERVER['REQUEST_SCHEME'] = "https";
53-
$_SERVER['SERVER_NAME'] = "storage-" . self::$createdUser['userId'] . ".example.com";
61+
$_SERVER['SERVER_NAME'] = "storage-" . self::$createdStorage['storageId'] . ".example.com";
5462
}
5563

5664
public function testGetFileSystem() {
@@ -72,12 +80,6 @@ public function testRespond() {
7280
$this->assertEquals($sentBody, "{\"Hello\":\"world\"}");
7381
}
7482

75-
public function testGetOwner() {
76-
$owner = StorageServer::getOwner();
77-
$this->assertEquals(self::$createdUser['webId'], $owner['webId']);
78-
$this->assertEquals(self::$createdUser['email'], $owner['email']);
79-
}
80-
8183
public function testGetOwnerWebId() {
8284
$webId = StorageServer::getOwnerWebId();
8385
$this->assertEquals(self::$createdUser['webId'], $webId);
@@ -123,6 +125,13 @@ public function testGenerateDefaultPreferences() {
123125
Currently untested:
124126
public static function getWebId($rawRequest) {
125127
public static function initializeStorage() {
128+
public static function getStorage($storageId) {
129+
public static function setStorageOwner($storageId, $owner) {
130+
public static function createStorage($ownerWebId) {
131+
public static function storageIdExists($storageId) {
126132
*/
127133
}
128134

135+
136+
137+

0 commit comments

Comments
 (0)