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