Skip to content
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

Add authentication support for WireMock Cloud (wiremock.io) #19

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/WireMock/Client/Authentication/Authenticator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace WireMock\Client\Authentication;

interface Authenticator
{
/**
* @param list<non-empty-string> $headers
* @return list<non-empty-string>
*/
public function modifyHeaders(array $headers): array;

/**
* @param non-empty-string $url
* @return non-empty-string
*/
public function modifyUrl(string $url): string;
}
16 changes: 16 additions & 0 deletions src/WireMock/Client/Authentication/NullAuthenticator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace WireMock\Client\Authentication;

final class NullAuthenticator implements Authenticator
{
public function modifyHeaders(array $headers): array
{
return $headers;
}

public function modifyUrl(string $url): string
{
return $url;
}
}
29 changes: 29 additions & 0 deletions src/WireMock/Client/Authentication/TokenAuthenticator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace WireMock\Client\Authentication;

final class TokenAuthenticator implements Authenticator
{
/**
* @var non-empty-string
*/
private $token;

/**
* @param non-empty-string $token
*/
public function __construct(string $token)
{
$this->token = $token;
}

public function modifyHeaders(array $headers): array
{
return array_merge($headers, [sprintf('Authorization: Token %s', $this->token)]);
}

public function modifyUrl(string $url): string
{
return $url;
}
}
23 changes: 19 additions & 4 deletions src/WireMock/Client/Curl.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@

namespace WireMock\Client;

use WireMock\Client\Authentication\Authenticator;
use WireMock\Client\Authentication\NullAuthenticator;

class Curl
{
/**
* @var Authenticator
*/
private $authenticator;

public function __construct(?Authenticator $authenticator = null)
{
$this->authenticator = $authenticator ?? new NullAuthenticator();
}

/**
* @param string $url
* @return string The response body
Expand Down Expand Up @@ -51,7 +64,7 @@ public function delete(string $url): string
*/
private function makeCurlRequest(string $method, string $url, ?string $json = null)
{
$ch = curl_init($url);
$ch = curl_init($this->authenticator->modifyUrl($url));
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($json !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
Expand All @@ -60,10 +73,12 @@ private function makeCurlRequest(string $method, string $url, ?string $json = nu
$contentLength = 0;
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(

$defaultHeaders = [
'Content-Type: application/json',
"Content-Length: $contentLength",
));
sprintf('Content-Length: %s', $contentLength),
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->authenticator->modifyHeaders($defaultHeaders));

$result = curl_exec($ch);

Expand Down
34 changes: 18 additions & 16 deletions src/WireMock/Client/HttpWait.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,35 @@

class HttpWait
{
/**
* @var Curl
*/
private $curl;

public function __construct(?Curl $curl = null)
{
$this->curl = $curl ?? new Curl();
}

public function waitForServerToGive200($url, $timeoutSecs = 10, $debug = true)
{
$debugTrace = array();
$startTime = microtime(true);
$serverStarted = false;
while (microtime(true) - $startTime < $timeoutSecs) {
try {
$headers = @get_headers($url, 1);
} catch (\Exception $e) {
$debugTrace[] = "$url not yet up. Error getting headers: " . $e->getMessage();
continue;
}

if (isset($headers) && isset($headers[0]) && strpos($headers[0], '200 OK') !== false) {
$this->curl->get($url);
$serverStarted = true;
break;
} else {
if (!isset($headers)) {
$debugTrace[] = "$url not yet up. \$headers not set";
} else if (!isset($headers[0])) {
$debugTrace[] = "$url not yet up. \$headers[0] not set";
} else {
$debugTrace[] = "$url not yet up. \$headers[0] was {$headers[0]}";
}
} catch (\Exception $e) {
$debugTrace[] = "$url not yet up. " . $e->getMessage();

usleep(100000);

continue;
}
usleep(100000);
}

if (!$serverStarted) {
$time = microtime(true) - $startTime;
if ($debug) {
Expand Down
2 changes: 1 addition & 1 deletion src/WireMock/Client/LogicalOperatorMatchingStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ public static function orAll(...$matchers): LogicalOperatorMatchingStrategy
{
return new self("or", $matchers);
}
}
}
18 changes: 11 additions & 7 deletions src/WireMock/Client/WireMock.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class WireMock
private $hostname;
/** @var int */
private $port;
/** @var string */
private $scheme;
/** @var HttpWait */
private $httpWait;
/** @var Curl */
Expand All @@ -45,13 +47,15 @@ public function __construct(
Curl $curl,
Serializer $serializer,
$hostname = 'localhost',
$port = 8080
$port = 8080,
$scheme = 'http'
) {
$this->hostname = $hostname;
$this->port = $port;
$this->httpWait = $httpWait;
$this->curl = $curl;
$this->serializer = $serializer;
$this->hostname = $hostname;
$this->port = $port;
$this->scheme = $scheme;
}

public function isAlive($timeoutSecs = 10, $debug = true)
Expand Down Expand Up @@ -98,11 +102,11 @@ public function importStubs($stubImportsOrBuilder)

/**
* @param RequestPatternBuilder|CountMatchingStrategy|integer $requestPatternBuilderOrCount
* @param RequestPatternBuilder|null $requestPatternBuilder
* @param ?RequestPatternBuilder $requestPatternBuilder
* @throws ClientException
* @throws VerificationException
*/
public function verify($requestPatternBuilderOrCount, RequestPatternBuilder $requestPatternBuilder = null)
public function verify($requestPatternBuilderOrCount, ?RequestPatternBuilder $requestPatternBuilder = null)
{
if ($requestPatternBuilderOrCount instanceof CountMatchingStrategy) {
$patternBuilder = $requestPatternBuilder;
Expand Down Expand Up @@ -506,9 +510,9 @@ private function doDelete(string $path, ?string $resultType = null)
}
}

private function _makeUrl($path)
private function _makeUrl($path): string
{
return "http://$this->hostname:$this->port/$path";
return sprintf('%s://%s:%d/%s', $this->scheme, $this->hostname, $this->port, $path);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use ReflectionException;
use ReflectionMethod;
use WireMock\Serde\ReflectionUtils;
use WireMock\Serde\SerializationException;
use WireMock\Serde\StaticFactoryMethodValidator;

Expand Down Expand Up @@ -47,8 +48,8 @@ function getSerializedName(array $data): string
*/
function getPossibleSerializedNames(): array
{
$refMethod = new ReflectionMethod($this->possibleNamesGenerator);
$refMethod = ReflectionUtils::reflectMethod($this->possibleNamesGenerator);
$refMethod->setAccessible(true);
return $refMethod->invoke(null);
}
}
}
20 changes: 20 additions & 0 deletions src/WireMock/Serde/ReflectionUtils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace WireMock\Serde;


use ReflectionMethod;

final class ReflectionUtils
{
public static function reflectMethod(string $methodName): ReflectionMethod
{
if (PHP_VERSION_ID >= 80300) {
$refMethod = ReflectionMethod::createFromMethodName($methodName);
} else {
$refMethod = new ReflectionMethod($methodName);
}

return $refMethod;
}
}
4 changes: 2 additions & 2 deletions src/WireMock/Serde/SerdeClassDiscriminationInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function __construct(string $discriminatorFactoryName, array $possibleSer
*/
public function getDiscriminator(): ?ClassDiscriminator
{
$refMethod = new ReflectionMethod($this->discriminatorFactoryName);
$refMethod = ReflectionUtils::reflectMethod($this->discriminatorFactoryName);
$refMethod->setAccessible(true);
return $refMethod->invoke(null);
}
Expand All @@ -51,4 +51,4 @@ public function getTypeByFullyQualifiedClassName(string $fqn): SerdeTypeClass
}
return $this->possibleSerdeTypes[$fqn];
}
}
}
4 changes: 2 additions & 2 deletions src/WireMock/Serde/SerdeProp.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function __construct(
string $name,
string $owningClassName,
SerdeType $serdeType,
PropertyNamingStrategy $propertyNamingStrategy = null,
?PropertyNamingStrategy $propertyNamingStrategy = null,
bool $unwrapped = false,
bool $catchAll = false
)
Expand Down Expand Up @@ -105,4 +105,4 @@ public function getPotentiallyNullableSerdeTypeClassOrThrow(): SerdeTypeClass {
}
throw new SerializationException("Cannot get SerdeTypeClass for prop $this->name because it is of type " . $this->serdeType->displayName());
}
}
}
4 changes: 2 additions & 2 deletions src/WireMock/Serde/StaticFactoryMethodValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class StaticFactoryMethodValidator
* @throws SerializationException
*/
public static function validate($fqMethodName) {
$refMethod = new ReflectionMethod($fqMethodName);
$refMethod = ReflectionUtils::reflectMethod($fqMethodName);
if (!$refMethod->isStatic()) {
throw new SerializationException("$fqMethodName must be a static method but is not");
}
Expand All @@ -21,4 +21,4 @@ public static function validate($fqMethodName) {
throw new SerializationException("$fqMethodName must take no required args, but requires $numRequiredParams");
}
}
}
}
27 changes: 27 additions & 0 deletions test/WireMock/Integration/CloudIntegrationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
namespace WireMock\Integration;

use PHPUnit\Framework\TestCase;
use WireMock\Client\Authentication\TokenAuthenticator;
use WireMock\Client\Curl;
use WireMock\Client\HttpWait;
use WireMock\Client\WireMock;
use WireMock\Serde\SerializerFactory;

class CloudIntegrationTest extends TestCase
{
public function testConnectToCloudHost(): void
{
$wiremockCloudHost = getenv('WIREMOCK_CLOUD_HOST');
$wiremockCloudToken = getenv('WIREMOCK_CLOUD_TOKEN');

if ($wiremockCloudToken === false || $wiremockCloudHost === false) {
self::markTestSkipped('Env variables WIREMOCK_CLOUD_HOST or WIREMOCK_CLOUD_TOKEN not set');
}

$curl = new Curl(new TokenAuthenticator($wiremockCloudToken));
$client = new WireMock(new HttpWait($curl), $curl, SerializerFactory::default(), $wiremockCloudHost, 443, 'https');

self::assertTrue($client->isAlive());
}
}