Skip to content

Commit bf4e171

Browse files
Merge pull request #13 from pdsinterop/dev
merge dev branch
2 parents f751c95 + d6e04f1 commit bf4e171

File tree

8 files changed

+284
-4
lines changed

8 files changed

+284
-4
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,17 @@ If you discover any security related issues, please email <[email protected]
147147
148148
-->
149149

150+
## Running solid/webid-provider-tests
151+
Due to https://github.com/pdsinterop/php-solid-server/issues/8 you should run, in one terminal window:
152+
```sh
153+
HOST=127.0.0.1 composer serve-dev
154+
```
155+
and in another you run the [webid-provider-test](https://github.com/solid/webid-provider-tests) as:
156+
```sh
157+
SERVER_ROOT=http://localhost:8080 ./node_modules/.bin/jest test/surface/fetch-openid-config.test.ts
158+
```
159+
The current `dev` branch of php-solid-server should pass roughly 7 out of 17 tests.
160+
150161
## Available Features
151162

152163
Based on the specifications, the following features are available:

composer.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"config": {
88
"bin-dir": "./bin",
99
"platform": {
10-
"php": "7.1.33",
10+
"php": "7.3.11",
1111
"ext-dom": "0.0.0",
1212
"ext-mbstring": "0.0.0"
1313
},
@@ -28,7 +28,8 @@
2828
"league/flysystem": "^1.0.",
2929
"league/oauth2-server": "^8.0",
3030
"league/route": "^4.5",
31-
"pdsinterop/flysystem-rdf": "^0.1",
31+
"pdsinterop/flysystem-rdf": "dev-dev",
32+
"pdsinterop/solid-auth": "dev-feature/implicit-grant",
3233
"php-http/httplug": "^2.1",
3334
"phptal/phptal": "^1.4"
3435
},
@@ -37,7 +38,7 @@
3738
},
3839
"scripts": {
3940
"lint":"",
40-
"serve-dev":"ENVIRONMENT=development php -S \"${HOST:-localhost}:${PORT:-8080}\" -t web/ web/index.php",
41+
"serve-dev":"USER=alice PASSWORD=alice123 ENVIRONMENT=development php -S \"${HOST:-localhost}:${PORT:-8080}\" -t web/ web/index.php",
4142
"serve-dev-docker":"bash ./bin/serve-docker-dev.sh",
4243
"test":"phpunit"
4344
},
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Pdsinterop\Solid\Controller;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
use Psr\Http\Message\ServerRequestInterface;
7+
8+
class AuthorizeController extends AbstractController
9+
{
10+
final public function __invoke(ServerRequestInterface $request, array $args): ResponseInterface
11+
{
12+
$httpHost = $request->getServerParams()['HTTP_HOST'];
13+
14+
// // Create a request
15+
// if (!$this->userManager->userExists($this->userId)) {
16+
// $result = new JSONResponse('Authorization required');
17+
// $result->setStatus(401);
18+
// return $result;
19+
// }
20+
21+
$parser = new \Lcobucci\JWT\Parser();
22+
$token = $parser->parse($_GET['request']);
23+
$_SESSION['token'] = $token;
24+
25+
$user = new \Pdsinterop\Solid\Auth\Entity\User();
26+
$user->setIdentifier('https://server/profile/card#me');
27+
28+
$getVars = $_GET;
29+
if (!isset($getVars['grant_type'])) {
30+
$getVars['grant_type'] = 'implicit';
31+
}
32+
$getVars['response_type'] = 'token';
33+
$getVars['scope'] = "openid";
34+
35+
if (!isset($getVars['redirect_uri'])) {
36+
$getVars['redirect_uri'] = 'https://solid.community/.well-known/solid/login';
37+
}
38+
$request = \Laminas\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $getVars, $_POST, $_COOKIE, $_FILES);
39+
$response = new \Laminas\Diactoros\Response();
40+
$server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response);
41+
42+
// if (!$this->checkApproval()) {
43+
// $result = new JSONResponse('Approval required');
44+
// $result->setStatus(302);
45+
// $result->addHeader("Location", $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.server.sharing")));
46+
// return $result;
47+
// }
48+
49+
// FIXME: check if the user has approved - if not, show approval screen;
50+
$approval = \Pdsinterop\Solid\Auth\Enum\Authorization::APPROVED;
51+
// $approval = false;
52+
return $server->respondToAuthorizationRequest($request, $user, $approval);
53+
}
54+
}

src/Controller/LoginController.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Pdsinterop\Solid\Controller;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
use Psr\Http\Message\ServerRequestInterface;
7+
8+
class LoginController extends AbstractController
9+
{
10+
final public function __invoke(ServerRequestInterface $request, array $args): ResponseInterface
11+
{
12+
$postBody = $request->getParsedBody();
13+
$response = $this->getResponse();
14+
// var_dump($_SESSION);
15+
if (isset($_SESSION['userid'])) {
16+
$user = $_SESSION['userid'];
17+
$response->getBody()->write("<h1>Already logged in as $user</h1>");
18+
} else if ($postBody['user'] == $_ENV['USER'] && $postBody['password'] == $_ENV['PASSWORD']) {
19+
$user = $postBody['user'];
20+
$response->getBody()->write("<h1>Welcome $user</h1>\n");
21+
$_SESSION['userid'] = $user;
22+
echo("session started\n");
23+
var_dump($_SESSION);
24+
} else {
25+
// var_dump($postBody);
26+
echo("cookie:\n");
27+
var_dump($_COOKIE);
28+
echo("session:\n");
29+
var_dump($_SESSION);
30+
$response->getBody()->write("<h1>No (try posting user=alice&password=alice123)</h1>\n");
31+
}
32+
return $response;
33+
}
34+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Pdsinterop\Solid\Controller;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
use Psr\Http\Message\ServerRequestInterface;
7+
8+
class OpenidController extends AbstractController
9+
{
10+
private $keys;
11+
12+
public function __construct(){
13+
// parent::__construct();
14+
require_once(__DIR__.'/../../vendor/autoload.php');
15+
16+
$this->keys = $this->getKeys();
17+
}
18+
private function linkToRoute($route) {
19+
return "/$route";
20+
}
21+
private function getBaseUrl($httpHost) {
22+
return "https://$httpHost";
23+
}
24+
private function getAbsoluteUrl($relativeUrl, $baseUrl) {
25+
return "$baseUrl$relativeUrl";
26+
}
27+
private function getOpenIdConfiguration($baseUrl) {
28+
return array(
29+
'issuer' => $baseUrl,
30+
'authorization_endpoint' => $this->getAbsoluteUrl($this->linkToRoute("authorize"), $baseUrl),
31+
'jwks_uri' => $this->getAbsoluteUrl($this->linkToRoute("jwks"), $baseUrl),
32+
"response_types_supported" => array("code","code token","code id_token","id_token code","id_token","id_token token","code id_token token","none"),
33+
"token_types_supported" => array("legacyPop","dpop"),
34+
"response_modes_supported" => array("query","fragment"),
35+
"grant_types_supported" => array("authorization_code","implicit","refresh_token","client_credentials"),
36+
"subject_types_supported" => ["public"],
37+
"id_token_signing_alg_values_supported" => ["RS256"],
38+
"token_endpoint_auth_methods_supported" => "client_secret_basic",
39+
"token_endpoint_auth_signing_alg_values_supported" => ["RS256"],
40+
"display_values_supported" => [],
41+
"claim_types_supported" => ["normal"],
42+
"claims_supported" => [],
43+
"claims_parameter_supported" => false,
44+
"request_parameter_supported" => true,
45+
"request_uri_parameter_supported" => false,
46+
"require_request_uri_registration" => false,
47+
"check_session_iframe" => $this->getAbsoluteUrl($this->linkToRoute("session"), $baseUrl),
48+
"end_session_endpoint" => $this->getAbsoluteUrl($this->linkToRoute("logout"), $baseUrl),
49+
"token_endpoint" => $this->getAbsoluteUrl($this->linkToRoute("token"), $baseUrl),
50+
"userinfo_endpoint" => $this->getAbsoluteUrl($this->linkToRoute("userinfo"), $baseUrl),
51+
"registration_endpoint" => $this->getAbsoluteUrl($this->linkToRoute("register"), $baseUrl),
52+
// "sharing_endpoint" => $this->getAbsoluteUrl($this->linkToRoute("sharing"), $baseUrl)
53+
);
54+
}
55+
private function getKeys() {
56+
// FIXME: read these from the solid config in nextcloud;
57+
$encryptionKey = 'P76gcBVeXsVzrHiYp4IIwore5rQz4cotdZ2j9GV5V04=';
58+
$privateKey = <<<EOF
59+
-----BEGIN RSA PRIVATE KEY-----
60+
MIIEpAIBAAKCAQEAvqb0htUFZaZ+z5rn7cHWg0VzsSoVnusbtJvwWtHfD0T0s6Hb
61+
OqzE5h2fgdGbB49HRtc21SNHx6jeEStGv03yyqYkLUKrJJSg+ksrL+pT3Nd0h25q
62+
sx7YUoPPxnm6sbd3XTg5efCb2yyV2dOoAegUPjK46Ra6PqUvmICQWDsjnv0VJIx+
63+
TdDWmKY2xElk0T6CVNMD08OZVTHPwJgpGdRZyCK/SSmrvmAZ6K3ocKySJdKgYriR
64+
bVMdx9NsczRkYU9b7tUpPmLu3IvsLboTbfRN23Y70Gx3Z8fuI1FRn23sEuQSIRW+
65+
NsAi7l+AEdJ7MdYn0xSY6YMNJ0/aGXi55gagQwIDAQABAoIBAQCz8CNNtnPXkqKR
66+
EmTfk1kAoGYmyc+KI+AMQDlDnlzmrnA9sf+Vi0Zy4XaQMeId6m6dP7Yyx4+Rs6GT
67+
lsK4/7qs5M20If4hEl40nQlvubvY7UjAIch2sh/9EQbjDjTUUpJH2y70FdEjtRrh
68+
cdBZrE6evYSkCZ1STtlzF7QkcfyWqilTHEntrHRaM3N+B6F74Yi5g6VyGE9uqKEM
69+
EuGDHVSXizdUjauTTVEa4o7pxTh+eTIdQsfRewer7iuxFPo2vBNOTU2O/obNUsVK
70+
mgmGM4QDjurgXLL2XPr0dVVo3eiFvIdmtZgGVyLfL/vUXH7bwUIfkV6qWyRmdBiY
71+
Dfsm8BJBAoGBAOGebDUVnP3NgFacWVYrtvBXcH2Q6X1W6JEAxctDDsnjchTdyG9E
72+
zcsMVM/gFKXIDF5VeNoSt2pwCTBL6K0oPC31c01clActbHStaJWOOCuifzrvmu4n
73+
X51TNGoKggbbSVx1UTifKte2t6SPRaZ26EqVrmO44fGkA3ip6TRYnSFzAoGBANhT
74+
J47EieRWiNflq9XqDAZ1fZzo3AHB+b+pO4r8GZr3Dw0ShCAnQXv7Gb2JAJvE3UrC
75+
Aq5r3yZMM7nI+n/OT06+UcJ3/vDGAPx9trNrpWkwmcWBmoBfp86vDRhT0kEIiKbO
76+
wLYMmSNLHNkmQQdBX2ytnsRxRyCWtQmm09bzOJHxAoGBAKEB/nSPnP5elfS5FOPy
77+
xFWWANgK/yWMTOGV7JFWpIocvz/22d/V+QqrHSdP4UxBi9oSIvF1I+FYXKZTtZNE
78+
wFWH8SXHKHhKyTgmvBjmal1xVFyJu0WzYX+TbjcykoI0IZFSw4ilxdw1L67G88yM
79+
1M7NLKtLuCpKgpOspZjOmCvTAoGAGji6KswYCt2SaNkmIx/jpUTInSR8xpnEtD7H
80+
QOmeEPKxmFwON/eKMIUXcaoRsNAEIvOxb4MT4YiLHJIIC0XuxxS6xF/XP0hBBloW
81+
s1jxC/cgLJixKa5uoNcHN1OxwMBQECgvo+GTDnwkWw4QA9kgwAOroxQ4EvMxrqHS
82+
O9Pvn4ECgYA7xr/3Sz8n+BhgOdABW0m91P144rK9QDYiaClSxAha1KiFunmAy3pB
83+
Uxdl4yTCTA9yKIH7X3bShDXnj+RmEZ+SkwzpPuKvAE8ZkZQuXv41anFrZYkR2PZy
84+
oYiERqXgH5yS/mkDeXRFx1nWsVxjoLWfd/Vi7Lr43cuYFy4UjqXZdg==
85+
-----END RSA PRIVATE KEY-----
86+
EOF;
87+
88+
$key = openssl_pkey_get_private($privateKey);
89+
$publicKey = openssl_pkey_get_details($key)['key'];
90+
91+
return array(
92+
"encryptionKey" => $encryptionKey,
93+
"privateKey" => $privateKey,
94+
"publicKey" => $publicKey
95+
);
96+
}
97+
private function getClientId() {
98+
return "coolApp";
99+
}
100+
private function getClient($clientId, $baseUrl) {
101+
if (!$clientId) {
102+
$clientId = $this->getClientId(); // FIXME: only continue if a clientId is set;
103+
}
104+
105+
if ($clientId) { // FIXME: and check that we know this client and get the client secret/client name for this client;
106+
$clientSecret = "super-secret-secret-squirrel";
107+
108+
// FIXME: use the redirect URIs as indicated by the client;
109+
$clientRedirectUris = array(
110+
$this->getAbsoluteURL($this->linkToRoute("token"), $baseUrl),
111+
'https://solid.community/.well-known/solid/login',
112+
'http://localhost:3001/redirect'
113+
);
114+
$clientName = "Nextcloud";
115+
116+
return new \Pdsinterop\Solid\Auth\Config\Client(
117+
$clientId,
118+
$clientSecret,
119+
$clientRedirectUris,
120+
$clientName
121+
);
122+
} else {
123+
return new \Pdsinterop\Solid\Auth\Config\Client('','',array(),'');
124+
}
125+
}
126+
127+
private function createConfig($baseUrl) {
128+
// if (isset($_GET['client_id'])) {
129+
$clientId = $_GET['client_id'];
130+
$client = $this->getClient($clientId, $baseUrl);
131+
// }
132+
try {
133+
$config = (new \Pdsinterop\Solid\Auth\Factory\ConfigFactory(
134+
$client,
135+
$this->keys['encryptionKey'],
136+
$this->keys['privateKey'],
137+
$this->keys['publicKey'],
138+
$this->openIdConfiguration
139+
))->create();
140+
} catch(\Throwable $e) {
141+
var_dump($e);
142+
}
143+
return $config;
144+
}
145+
146+
final public function __invoke(ServerRequestInterface $request, array $args): ResponseInterface
147+
{
148+
$httpHost = $request->getServerParams()['HTTP_HOST'];
149+
$baseUrl = $this->getBaseUrl($httpHost);
150+
$this->openIdConfiguration = $this->getOpenIdConfiguration($baseUrl);
151+
152+
$this->authServerConfig = $this->createConfig($baseUrl);
153+
$this->authServerFactory = (new \Pdsinterop\Solid\Auth\Factory\AuthorizationServerFactory($this->authServerConfig))->create();
154+
155+
$response = $this->getResponse();
156+
$server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response);
157+
return $server->respondToOpenIdMetadataRequest();
158+
}
159+
}

src/Controller/Profile/CardController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ final public function __invoke(ServerRequestInterface $request, array $args): Re
4848
$contentType = $this->getContentTypeForFormat($format);
4949

5050
/** @noinspection PhpUndefinedMethodInspection */ // Method `readRdf` is defined by plugin
51-
$content = $filesystem->readRdf($filePath, $format);
51+
$url = $request->getServerParams()["REQUEST_URI"];
52+
$content = $filesystem->readRdf($filePath, $format, $url);
5253

5354
return $this->createTextResponse($content)->withHeader('Content-Type', $contentType);
5455
}

tests/fixtures/foaf.rdf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,18 @@
33
xmlns:foaf="http://xmlns.com/foaf/0.1/"
44
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
55
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
6+
xmlns:ldp="http://www.w3.org/ns/ldp#"
7+
xmlns:solid="http://www.w3.org/ns/solid/terms#"
8+
xmlns:space="http://www.w3.org/ns/pim/space#"
69
>
710
<foaf:Person rdf:ID="me">
11+
<ldp:inbox rdf:resource="/inbox/"/>
12+
<solid:account rdf:resource="/"/>
13+
<space:storage rdf:resource="/"/>
14+
<solid:privateTypeIndex rdf:resource="/settings/privateTypeIndex.ttl"/>
15+
<solid:publicTypeIndex rdf:resource="/settings/publicTypeIndex.ttl"/>
16+
<space:preferencesFile rdf:resource="/settings/preferencesFile.ttl"/>
17+
<rdf:type rdf:resource="http://schema.org/Person"/>
818
<foaf:depiction rdf:resource="https://www.gravatar.com/avatar/f8d7c4a4899736c59ec1e40c7021d477?s=1024"/>
919
<foaf:family_name>Peachey</foaf:family_name>
1020
<foaf:givenname>Ben</foaf:givenname>

web/index.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
use League\Route\Http\Exception\NotFoundException;
1717
use League\Route\Router;
1818
use League\Route\Strategy\ApplicationStrategy;
19+
use Pdsinterop\Solid\Controller\LoginController;
1920
use Pdsinterop\Solid\Controller\AddSlashToPathController;
2021
use Pdsinterop\Solid\Controller\HelloWorldController;
2122
use Pdsinterop\Solid\Controller\HttpToHttpsController;
2223
use Pdsinterop\Solid\Controller\Profile\CardController;
2324
use Pdsinterop\Solid\Controller\Profile\ProfileController;
25+
use Pdsinterop\Solid\Controller\OpenidController;
26+
use Pdsinterop\Solid\Controller\AuthorizeController;
2427
use Psr\Http\Message\ResponseInterface;
2528
use Psr\Http\Message\ServerRequestInterface;
2629

@@ -32,6 +35,7 @@
3235
);
3336
$strategy = new ApplicationStrategy();
3437

38+
session_start();
3539
$router = new Router();
3640

3741
/*/ Wire objects together /*/
@@ -68,11 +72,14 @@
6872
});
6973

7074
$controllers = [
75+
LoginController::class,
7176
AddSlashToPathController::class,
7277
CardController::class,
7378
HelloWorldController::class,
7479
HttpToHttpsController::class,
7580
ProfileController::class,
81+
OpenidController::class,
82+
AuthorizeController::class
7683
];
7784

7885
$traits = [
@@ -110,10 +117,13 @@
110117
$router->map('GET', '/', HelloWorldController::class)->setScheme($scheme);
111118

112119
/*/ Create URI groups /*/
120+
$router->map('POST', '/login', LoginController::class)->setScheme($scheme);
113121
$router->map('GET', '/profile', AddSlashToPathController::class)->setScheme($scheme);
114122
$router->map('GET', '/profile/', ProfileController::class)->setScheme($scheme);
115123
$router->map('GET', '/profile/card', CardController::class)->setScheme($scheme);
116124
$router->map('GET', '/profile/card{extension}', CardController::class)->setScheme($scheme);
125+
$router->map('GET', '/.well-known/openid-configuration', OpenidController::class)->setScheme($scheme);
126+
$router->map('GET', '/authorize', AuthorizeController::class)->setScheme($scheme);
117127

118128
try {
119129
$response = $router->dispatch($request);

0 commit comments

Comments
 (0)