diff --git a/README.md b/README.md index ee8a607..33826af 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,26 @@ For the user management, no framework is used to keep the codebase lean. Start the docker containers: ```sh -docker-compose up +docker compose up ``` This will start up three containers: the solid server, pubsub server and a mailpit server. If you have an actual SMTP server running, feel free to remove the mailpit container. The persisted data will be stored in the data/ directory. This contains the keys, pods, db and mailpit data. -Run the following commands to set up the container (replace 'solid' below with the name of your container): -Note: Update the values in the config.php file where needed befure running the init script. +Run the following commands to set up the container: +Note: Update the values in the config.php file where needed before running the init script. ```sh -docker exec -w /opt/solid/ solid cp config.php.example config.php -docker exec -u www-data -i -w /opt/solid/ solid php init.php -docker exec -w /opt/solid/ solid chown -R www-data:www-data keys pods profiles db +docker compose exec -w /opt/solid/ solid cp config.php.example config.php +docker compose exec -w /opt/solid/ solid chown -R www-data:www-data keys pods profiles db +docker compose exec -u www-data -i -w /opt/solid/ solid php init.php +``` + +If you need dev user accounts bob and alice, also run this: + +```sh +docker compose exec -u www-data -i -w /opt/solid/ solid php init-devusers.php +docker compose exec -w /opt/solid/ solid chown -R www-data:www-data keys pods profiles db # again ``` Now add the following host to your `/etc/hosts` file: @@ -30,7 +37,7 @@ Now add the following host to your `/etc/hosts` file: 127.0.0.1 solid.local ``` -And browser to `https://solid.local/`. After you register a new account, you'll get an identity and storage hostname, add these to `/etc/hosts` as well, e.g: +And browse to `https://solid.local/`. After you register a new account, you'll get an identity and storage hostname, add these to `/etc/hosts` as well, e.g: ``` 127.0.0.1 id-d1f0e8c54e755cb45b61ee8e9dad00fe.solid.local storage-d1f0e8c54e755cb45b61ee8e9dad00fe.solid.local ``` diff --git a/docker-compose.yml b/docker-compose.yml index e2d00c1..de7b820 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3' services: solid: build: @@ -11,6 +10,10 @@ services: - ./data/db:/opt/solid/db - ./data/pods:/opt/solid/pods - ./data/profiles:/opt/solid/profiles + - ./frontend:/opt/solid/frontend + - ./lib:/opt/solid/lib + - ./www/idp:/opt/solid/www/idp + - ./config.php:/opt/solid/config.php pubsub: build: context: https://github.com/pdsinterop/php-solid-pubsub-server.git#main diff --git a/init-devusers.php b/init-devusers.php new file mode 100644 index 0000000..ddffbfb --- /dev/null +++ b/init-devusers.php @@ -0,0 +1,50 @@ +prepare('INSERT INTO storage VALUES(:storageId, :owner)'); + $query->execute([ + ':storageId' => $storageId, + ':owner' => $ownerWebId + ]); + } catch(\PDOException $e) { + echo $e->getMessage(); + } + } + + function addUser($userId) { + try { + $pdo = new \PDO("sqlite:" . DBPATH); + $query = $pdo->prepare('INSERT INTO users VALUES (:userId, :email, :passwordHash, :data)'); + $webId = "https://id-" . $userId . "." . BASEDOMAIN . "/#me"; + $userData = [ + "id" => $userId, + "email" => $userId, + "webId" => $webId + ]; + $query->execute([ + ':userId' => $userId, + ':email' => $userId, + ':passwordHash' => password_hash($userId, PASSWORD_BCRYPT), + ':data' => json_encode($userData) + ]); + } catch(\PDOException $e) { + echo $e->getMessage(); + } + return $webId; + } + + $users = [ + 'alice', + 'bob' + ]; + + foreach ($users as $user) { + $webId = addUser($user); + addStorage($user, $webId); + } + \ No newline at end of file diff --git a/lib/Routes/Account.php b/lib/Routes/Account.php index 55e08df..070d4b7 100644 --- a/lib/Routes/Account.php +++ b/lib/Routes/Account.php @@ -71,7 +71,7 @@ public static function respondToAccountNew() { header("HTTP/1.1 400 Bad Request"); exit(); } - if (!$_POST['password'] === $_POST['repeat_password']) { + if ($_POST['password'] !== $_POST['repeat_password']) { error_log("Password repeat does not match"); header("HTTP/1.1 400 Bad Request"); exit(); diff --git a/lib/Routes/SolidStorage.php b/lib/Routes/SolidStorage.php index 1201725..9679703 100644 --- a/lib/Routes/SolidStorage.php +++ b/lib/Routes/SolidStorage.php @@ -1,86 +1,21 @@ fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); +use Laminas\Diactoros\ServerRequestFactory; +use Pdsinterop\PhpSolid\SolidStorageHandler; +use Pdsinterop\PhpSolid\StorageServer; - try { - StorageServer::initializeStorage(); - $filesystem = StorageServer::getFileSystem(); - } catch (\Exception $e) { - $response = new Response(); - $response = $response->withStatus(404, "Not found"); - StorageServer::respond($response); - exit(); - } +class SolidStorage +{ + public static function respondToStorage() + { + $requestFactory = new ServerRequestFactory(); + $rawRequest = $requestFactory->fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); - $resourceServer = new ResourceServer($filesystem, new Response(), null); - $solidNotifications = new SolidNotifications(); - $resourceServer->setNotifications($solidNotifications); + $handler = new SolidStorageHandler(); + $response = $handler->handle($rawRequest); - $wac = new WAC($filesystem); - - $baseUrl = Util::getServerBaseUrl(); - $resourceServer->setBaseUrl($baseUrl); - $wac->setBaseUrl($baseUrl); - - try { - $webId = StorageServer::getWebId($rawRequest); - } catch(\Exception $e) { - $response = $resourceServer->getResponse() - -> withStatus(400, "Bad request"); - StorageServer::respond($response); - exit(); - } - - if (!isset($webId)) { - $response = $resourceServer->getResponse() - ->withStatus(409, "Invalid token"); - StorageServer::respond($response); - exit(); - } - - $origin = $rawRequest->getHeaderLine("Origin"); - - // FIXME: Read allowed clients from the profile instead; - - $ownerWebId = StorageServer::getOwnerWebId(); - $owner = User::getUserByWebId($ownerWebId); - - $allowedClients = $owner['allowedClients'] ?? []; - $allowedOrigins = array_merge( - ($owner['allowedOrigins'] ?? []), - (TRUSTED_APPS ?? []) - ); - $allowedOrigins = array_unique($allowedOrigins); - - if (!isset($origin) || ($origin === "")) { - $allowedOrigins[] = "app://unset"; // FIXME: this should not be here. - $origin = "app://unset"; - } - - if (!$wac->isAllowed($rawRequest, $webId, $origin, $allowedOrigins)) { - $response = new Response(); - $response = $response->withStatus(403, "Access denied!"); - StorageServer::respond($response); - exit(); - } - - $response = $resourceServer->respondToRequest($rawRequest); - $response = $wac->addWACHeaders($rawRequest, $response, $webId); - StorageServer::respond($response); - } + StorageServer::respond($response); } - \ No newline at end of file +} diff --git a/lib/Server.php b/lib/Server.php index ab128a8..33c3c02 100644 --- a/lib/Server.php +++ b/lib/Server.php @@ -66,7 +66,12 @@ public static function getConfigClient() { if ($clientId) { $registeredClient = ClientRegistration::getRegistration($clientId); } - if (isset($registeredClient)) { + if (isset($registeredClient)) { + if (!$registeredClient || !isset($registeredClient['redirect_uris'])) { + //TODO: better to throw an error and handle that on the outside + header("HTTP/1.1 400 Bad request"); + exit(); + } return new ConfigClient( $clientId, $registeredClient['client_secret'] ?? '', diff --git a/lib/SolidStorageHandler.php b/lib/SolidStorageHandler.php new file mode 100644 index 0000000..2901287 --- /dev/null +++ b/lib/SolidStorageHandler.php @@ -0,0 +1,71 @@ +withStatus(404, "Not found"); + } + + $resourceServer = new ResourceServer($filesystem, new Response(), null); + + $solidNotifications = new SolidNotifications(); + $resourceServer->setNotifications($solidNotifications); + + $wac = new WAC($filesystem); + + $baseUrl = Util::getServerBaseUrl(); + $resourceServer->setBaseUrl($baseUrl); + $wac->setBaseUrl($baseUrl); + + try { + $webId = StorageServer::getWebId($rawRequest); + } catch (\Exception $e) { + return $resourceServer->getResponse() + ->withStatus(400, "Bad request"); + } + + if (!isset($webId)) { + return $resourceServer->getResponse() + ->withStatus(409, "Invalid token"); + } + + $origin = $rawRequest->getHeaderLine("Origin"); + + // FIXME: Read allowed clients from the profile instead; + $ownerWebId = StorageServer::getOwnerWebId(); + $owner = User::getUserByWebId($ownerWebId); + $allowedClients = $owner['allowedClients'] ?? []; + + $allowedOrigins = array_merge( + ($owner['allowedOrigins'] ?? []), + (TRUSTED_APPS ?? []) + ); + $allowedOrigins = array_unique($allowedOrigins); + + if (!isset($origin) || ($origin === "")) { + $allowedOrigins[] = "app://unset"; // FIXME: this should not be here. + $origin = "app://unset"; + } + + if (!$wac->isAllowed($rawRequest, $webId, $origin, $allowedOrigins)) { + return (new Response())->withStatus(403, "Access denied!"); + } + + $response = $resourceServer->respondToRequest($rawRequest); + + return $wac->addWACHeaders($rawRequest, $response, $webId); + } +}