Skip to content

Add TLS Support and Connection Timeout Handling to ConnectionPool #263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
45 changes: 36 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,48 @@
ARG PHP_VERSION
ARG PHP_VERSION=8.4.7

FROM php:${PHP_VERSION}-cli

# Set environment variables
ENV PATH="/usr/local/go/bin:${PATH}" \
COMPOSER_ALLOW_SUPERUSER=1 \
DEBIAN_FRONTEND=noninteractive

# Install dependencies, Go, PHP extensions, Python venv and tools
RUN apt-get update \
&& apt-get install -y \
&& apt-get install -y --no-install-recommends \
libzip-dev \
unzip \
git \
wget \
curl \
tar \
gcc \
make \
ca-certificates \
build-essential \
pkg-config \
software-properties-common \
python3-venv \
python3-pip \
python3-setuptools \
# Install Go 1.22.0
&& curl -LO https://golang.org/dl/go1.22.0.linux-amd64.tar.gz \
&& tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz \
&& rm go1.22.0.linux-amd64.tar.gz \
&& ln -s /usr/local/go/bin/go /usr/bin/go \
# Install PHP extensions
&& docker-php-ext-install -j$(nproc) bcmath sockets \
&& wget https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 \
&& mv test-reporter-latest-linux-amd64 /usr/bin/cc-test-reporter \
# Install CodeClimate Test Reporter
&& wget -O /usr/bin/cc-test-reporter https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 \
&& chmod +x /usr/bin/cc-test-reporter \
# Install and enable Xdebug
&& pecl install xdebug \
&& docker-php-ext-enable xdebug && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
&& docker-php-ext-enable xdebug \
# Install Composer
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
# Cleanup
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Set working directory
WORKDIR /opt/project



12 changes: 6 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ x-definitions:
PHP_VERSION: "${PHP_VERSION-8.1}"
volumes:
- .:/opt/project

x-common-cluster:
&common-cluster
<<: *common
Expand All @@ -51,9 +52,10 @@ services:
- .:/opt/project
env_file:
- .env

neo4j:
<<: *common
image: neo4j:5.23-community
image: neo4j:5-enterprise
hostname: neo4j
networks:
- neo4j
Expand All @@ -62,11 +64,10 @@ services:
- "11474:7474"
environment:
<<: *common-env
NEO4J_ACCEPT_LICENSE_AGREEMENT: 'yes'
NEO4J_server_bolt_advertised__address: neo4j:7687
NEO4J_server_http_advertised__address: neo4j:7474



server1:
<<: *common-cluster
hostname: server1
Expand Down Expand Up @@ -117,9 +118,7 @@ services:
NEO4J_server_http_advertised__address: server4:7474

testkit:
image: python:3.13
volumes:
- .:/opt/project
<<: *common-php
working_dir: /opt/project/testkit-backend
networks:
- neo4j
Expand All @@ -140,6 +139,7 @@ services:
- neo4j
extra_hosts:
- "host.docker.internal:host-gateway"
- "thehost:host-gateway"
depends_on:
- neo4j
ports:
Expand Down
36 changes: 11 additions & 25 deletions src/Authentication/BasicAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,10 @@

namespace Laudis\Neo4j\Authentication;

use Bolt\protocol\V4_4;
use Bolt\protocol\V5;
use Bolt\protocol\V5_1;
use Bolt\protocol\V5_2;
use Bolt\protocol\V5_3;
use Bolt\protocol\V5_4;
use Exception;
use Laudis\Neo4j\Bolt\BoltConnection;
use Laudis\Neo4j\Bolt\BoltMessageFactory;
use Laudis\Neo4j\Common\Neo4jLogger;
use Laudis\Neo4j\Common\ResponseHelper;
use Laudis\Neo4j\Contracts\AuthenticateInterface;
use Psr\Http\Message\UriInterface;

Expand All @@ -43,27 +37,26 @@ public function __construct(
*
* @return array{server: string, connection_id: string, hints: list}
*/
public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $userAgent): array
public function authenticateBolt(BoltConnection $connection, string $userAgent): array
{
$factory = $this->createMessageFactory($protocol);
$factory = $this->createMessageFactory($connection);

$protocol = $connection->protocol();
if (method_exists($protocol, 'logon')) {
$helloMetadata = ['user_agent' => $userAgent];

$factory->createHelloMessage($helloMetadata)->send();
$response = ResponseHelper::getResponse($protocol);
$responseHello = $factory->createHelloMessage($helloMetadata)->send()->getResponse();

$credentials = [
'scheme' => 'basic',
'principal' => $this->username,
'credentials' => $this->password,
];

$factory->createLogonMessage($credentials)->send();
ResponseHelper::getResponse($protocol);
$response = $factory->createLogonMessage($credentials)->send()->getResponse();

/** @var array{server: string, connection_id: string, hints: list} */
return $response->content;
return array_merge($responseHello->content, $response->content);
}

$helloMetadata = [
Expand All @@ -73,22 +66,15 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $
'credentials' => $this->password,
];

$factory->createHelloMessage($helloMetadata)->send();
$response = $factory->createHelloMessage($helloMetadata)->send()->getResponse();

/** @var array{server: string, connection_id: string, hints: list} */
return ResponseHelper::getResponse($protocol)->content;
return $response->content;
}

/**
* @throws Exception
*/
public function logoff(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): void
{
$factory = $this->createMessageFactory($protocol);
$factory->createLogoffMessage()->send();
ResponseHelper::getResponse($protocol);
}

public function toString(UriInterface $uri): string
{
return sprintf('Basic %s:%s@%s:%s', $this->username, '######', $uri->getHost(), $uri->getPort() ?? '');
Expand All @@ -97,8 +83,8 @@ public function toString(UriInterface $uri): string
/**
* Helper to create message factory.
*/
private function createMessageFactory(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): BoltMessageFactory
private function createMessageFactory(BoltConnection $connection): BoltMessageFactory
{
return new BoltMessageFactory($protocol, $this->logger);
return new BoltMessageFactory($connection, $this->logger);
}
}
35 changes: 8 additions & 27 deletions src/Authentication/KerberosAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,11 @@

namespace Laudis\Neo4j\Authentication;

use Bolt\protocol\V4_4;
use Bolt\protocol\V5;
use Bolt\protocol\V5_1;
use Bolt\protocol\V5_2;
use Bolt\protocol\V5_3;
use Bolt\protocol\V5_4;
use Exception;
use Laudis\Neo4j\Bolt\BoltConnection;
use Laudis\Neo4j\Bolt\BoltMessageFactory;
use Laudis\Neo4j\Common\Neo4jLogger;
use Laudis\Neo4j\Common\ResponseHelper;
use Laudis\Neo4j\Contracts\AuthenticateInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
use Psr\Log\LogLevel;

Expand All @@ -41,38 +34,26 @@ public function __construct(
) {
}

public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface
{
$this->logger?->log(LogLevel::DEBUG, 'Authenticating using KerberosAuth');

return $request->withHeader('Authorization', 'Kerberos '.$this->token)
->withHeader('User-Agent', $userAgent);
}

/**
* @throws Exception
*
* @return array{server: string, connection_id: string, hints: list}
*/
public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $userAgent): array
public function authenticateBolt(BoltConnection $connection, string $userAgent): array
{
$factory = $this->createMessageFactory($protocol);
$factory = $this->createMessageFactory($connection);

$this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent]);

$factory->createHelloMessage(['user_agent' => $userAgent])->send();

$response = ResponseHelper::getResponse($protocol);
$factory->createHelloMessage(['user_agent' => $userAgent])->send()->getResponse();

$this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'kerberos', 'principal' => '']);

$factory->createLogonMessage([
$response = $factory->createLogonMessage([
'scheme' => 'kerberos',
'principal' => '',
'credentials' => $this->token,
])->send();

ResponseHelper::getResponse($protocol);
])->send()->getResponse();

/**
* @var array{server: string, connection_id: string, hints: list}
Expand All @@ -88,8 +69,8 @@ public function toString(UriInterface $uri): string
/**
* Helper to create the message factory.
*/
private function createMessageFactory(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): BoltMessageFactory
private function createMessageFactory(BoltConnection $connection): BoltMessageFactory
{
return new BoltMessageFactory($protocol, $this->logger);
return new BoltMessageFactory($connection, $this->logger);
}
}
45 changes: 11 additions & 34 deletions src/Authentication/NoAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,13 @@

namespace Laudis\Neo4j\Authentication;

use Bolt\protocol\V4_4;
use Bolt\protocol\V5;
use Bolt\protocol\V5_1;
use Bolt\protocol\V5_2;
use Bolt\protocol\V5_3;
use Bolt\protocol\V5_4;
use Exception;
use Laudis\Neo4j\Bolt\BoltConnection;
use Laudis\Neo4j\Bolt\BoltMessageFactory;
use Laudis\Neo4j\Common\Neo4jLogger;
use Laudis\Neo4j\Common\ResponseHelper;
use Laudis\Neo4j\Contracts\AuthenticateInterface;
use Psr\Http\Message\RequestInterface;
use Laudis\Neo4j\Enum\ConnectionProtocol;
use Psr\Http\Message\UriInterface;
use Psr\Log\LogLevel;

use function sprintf;

Expand All @@ -37,30 +30,21 @@ public function __construct(
) {
}

public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface
{
$this->logger?->log(LogLevel::DEBUG, 'Authentication disabled');

return $request->withHeader('User-Agent', $userAgent);
}

/**
* @throws Exception
*
* @return array{server: string, connection_id: string, hints: list}
*/
public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $userAgent): array
public function authenticateBolt(BoltConnection $connection, string $userAgent): array
{
$factory = $this->createMessageFactory($protocol);
$factory = $this->createMessageFactory($connection);

if (method_exists($protocol, 'logon')) {
if ($connection->getProtocol()->compare(ConnectionProtocol::BOLT_V5_1()) >= 0) {
$helloMetadata = ['user_agent' => $userAgent];

$factory->createHelloMessage($helloMetadata)->send();
$response = ResponseHelper::getResponse($protocol);
$factory->createHelloMessage($helloMetadata)->send()->getResponse();

$factory->createLogonMessage(['scheme' => 'none'])->send();
ResponseHelper::getResponse($protocol);
$response = $factory->createLogonMessage(['scheme' => 'none'])->send()->getResponse();

/** @var array{server: string, connection_id: string, hints: list} */
return $response->content;
Expand All @@ -71,26 +55,19 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $
'scheme' => 'none',
];

$factory->createHelloMessage($helloMetadata)->send();
$response = $factory->createHelloMessage($helloMetadata)->send()->getResponse();

/** @var array{server: string, connection_id: string, hints: list} */
return ResponseHelper::getResponse($protocol)->content;
}

public function logoff(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): void
{
$factory = $this->createMessageFactory($protocol);
$factory->createLogoffMessage()->send();
ResponseHelper::getResponse($protocol);
return $response->content;
}

public function toString(UriInterface $uri): string
{
return sprintf('No Auth %s:%s', $uri->getHost(), $uri->getPort() ?? '');
}

private function createMessageFactory(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): BoltMessageFactory
private function createMessageFactory(BoltConnection $connection): BoltMessageFactory
{
return new BoltMessageFactory($protocol, $this->logger);
return new BoltMessageFactory($connection, $this->logger);
}
}
Loading