Misc fixes, readme, comments
This commit is contained in:
@ -7,4 +7,5 @@ use Exception;
|
||||
class SecurityException extends Exception
|
||||
{
|
||||
const ERR_ACCESS_DENIED = 50001;
|
||||
const ERR_NO_PERMISSION = 50002;
|
||||
}
|
@ -80,6 +80,7 @@ class Server
|
||||
*/
|
||||
private function createHttpServer(array $options): HttpServer
|
||||
{
|
||||
// TODO break out the middleware to facilitate testing
|
||||
return new HttpServer(
|
||||
$this->rejectionWrappingMiddleware(...),
|
||||
$this->checkRequestSecurityMiddleware(...),
|
||||
@ -91,7 +92,7 @@ class Server
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Resolves unhandled requests with a 404 error
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return PromiseInterface
|
||||
@ -106,7 +107,8 @@ class Server
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Wraps rejections into error messages, and also does some sanity checks on the returned
|
||||
* data, making sure it is a response.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param callable $next
|
||||
@ -275,6 +277,22 @@ class Server
|
||||
throw new \Exception("Invalid request");
|
||||
}
|
||||
|
||||
// Parse out the urlencoded body. Pretty sure there is a better way to do this?
|
||||
$body = explode("&", (string)$request->getBody());
|
||||
$data = [];
|
||||
foreach ($body as $param) {
|
||||
if (!str_contains($param, "="))
|
||||
throw new RequestException("Invalid request data", RequestException::ERR_INVALID_REQUEST_DATA);
|
||||
[ $name, $value ] = array_map('urldecode', explode("=", $param, 2));
|
||||
if (in_array($name, [ 'topic' ])) {
|
||||
if (!isset($data[$name]))
|
||||
$data[$name] = [];
|
||||
$data[$name][] = $value;
|
||||
} else {
|
||||
$data[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the JWT token from the requests authorization attribute
|
||||
$tok = $request->getAttribute('authorization');
|
||||
if ($tok instanceof JWTToken) {
|
||||
@ -282,22 +300,9 @@ class Server
|
||||
if (isset($claims['mercure']['publish'])) {
|
||||
$publishClaims = $claims['mercure']['publish'];
|
||||
// TODO check topic against publishClaims
|
||||
}
|
||||
}
|
||||
|
||||
// Parse out the urlencoded body. Pretty sure there is a better way to do this?
|
||||
$body = explode("&", (string)$request->getBody());
|
||||
$data = [];
|
||||
foreach ($body as $param) {
|
||||
if (!str_contains($param, "=")) throw new RequestException("Invalid request data", RequestException::ERR_INVALID_REQUEST_DATA);
|
||||
[ $name, $value ] = array_map('urldecode', explode("=", $param, 2));
|
||||
// FIXME support multiple topics?
|
||||
if (in_array($name, [ 'topic' ])) {
|
||||
if (!isset($data[$name]))
|
||||
$data[$name] = [];
|
||||
$data[$name][] = $value;
|
||||
} else {
|
||||
$data[$name] = $value;
|
||||
if (!$this->checkTopicClaims($data['topic']??[], $publishClaims)) {
|
||||
throw new SecurityException("Insufficient permissions for publish", SecurityException::ERR_NO_PERMISSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,6 +321,18 @@ class Server
|
||||
return Response::plaintext("urn:uuid:".$message->id."\n");
|
||||
}
|
||||
|
||||
private function checkTopicClaims(string|array $topic, array $claims): bool
|
||||
{
|
||||
foreach ((array)$topic as $match) {
|
||||
foreach ($claims as $claim) {
|
||||
if ($claim === "*") return true;
|
||||
if ($claim === $match) return true;
|
||||
// TODO implement full matching
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
@ -327,7 +344,7 @@ class Server
|
||||
foreach ($this->webSocketClients as $webSocket) {
|
||||
$webSocket->write(json_encode([
|
||||
'type' => $message->type,
|
||||
//'topic' => $data['topic'],
|
||||
'topic' => $message->topic,
|
||||
'data' => (@json_decode($message->data))??$message->data
|
||||
]));
|
||||
}
|
||||
|
Reference in New Issue
Block a user