Skip to content

Commit 3015911

Browse files
authored
Merge pull request #35 from sroze/feature/add-client-certificate-authentication
Allow client-certificate authentication
2 parents 5d68f23 + 80131ab commit 3015911

6 files changed

Lines changed: 85 additions & 31 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ To had user authentication, you can decorate the http client:
2424
```php
2525
$authenticatedHttpClient = new AuthenticationMiddleware(
2626
$httpClient,
27-
'username',
28-
'password'
27+
AuthenticationMiddleware::USERNAME_PASSWORD,
28+
'username:password'
2929
);
3030
```
3131

features/bootstrap/ClientContext.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,25 @@ class ClientContext implements Context, SnippetAcceptingContext
3131
* @param string $version
3232
* @param string $usernameOrToken
3333
* @param string $password
34+
* @param bool $integration
35+
* @param bool $record
36+
* @param string|null $caCertificate
3437
*/
35-
public function __construct($baseUrl, $version, $usernameOrToken = null, $password = null, $integration = false, $record = false)
38+
public function __construct($baseUrl, $version, $usernameOrToken = null, $password = null, $integration = false, $record = false, $caCertificate = null)
3639
{
3740
$serializer = SerializerBuilder::create()
3841
->addMetadataDir(__DIR__.'/../../src/Resources/serializer', 'Kubernetes\Client')
3942
->build();
4043

4144
if ($integration) {
42-
$httpClient = new GuzzleHttpClient(new GuzzleClient([
43-
'verify' => false,
44-
]), $baseUrl, $version);
45+
$httpClient = new GuzzleHttpClient(
46+
new GuzzleClient([
47+
'verify' => false,
48+
]),
49+
$baseUrl,
50+
$version,
51+
$caCertificate
52+
);
4553
} else {
4654
$httpClient = new FileHttpClient(new FileResolver());
4755
}
@@ -53,8 +61,14 @@ public function __construct($baseUrl, $version, $usernameOrToken = null, $passwo
5361
);
5462
}
5563

56-
if ($usernameOrToken !== null) {
57-
$httpClient = new AuthenticationMiddleware($httpClient, $usernameOrToken, $password);
64+
if ($password !== null) {
65+
if ($usernameOrToken == 'cert') {
66+
$httpClient = new AuthenticationMiddleware($httpClient, AuthenticationMiddleware::CERTIFICATE, $password);
67+
} else {
68+
$httpClient = new AuthenticationMiddleware($httpClient, AuthenticationMiddleware::USERNAME_PASSWORD, $usernameOrToken . ':' . $password);
69+
}
70+
} elseif ($usernameOrToken !== null) {
71+
$httpClient = new AuthenticationMiddleware($httpClient, AuthenticationMiddleware::TOKEN, $usernameOrToken);
5872
}
5973

6074
$connector = new HttpConnector(

spec/Adapter/Http/AuthenticationMiddlewareSpec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class AuthenticationMiddlewareSpec extends ObjectBehavior
1313
{
1414
function let(HttpClient $httpClient)
1515
{
16-
$this->beConstructedWith($httpClient, 'username', 'password');
16+
$this->beConstructedWith($httpClient, AuthenticationMiddleware::USERNAME_PASSWORD, 'username:password');
1717
}
1818

1919
function it_can_do_an_authenticated_request(HttpClient $httpClient)

src/Adapter/Http/AuthenticationMiddleware.php

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22

33
namespace Kubernetes\Client\Adapter\Http;
44

5+
require_once 'functions.php';
6+
57
class AuthenticationMiddleware implements HttpClient
68
{
9+
const USERNAME_PASSWORD = 'username:password';
10+
const TOKEN = 'token';
11+
const CERTIFICATE = 'certificate';
12+
713
/**
814
* @var HttpClient
915
*/
@@ -12,73 +18,79 @@ class AuthenticationMiddleware implements HttpClient
1218
/**
1319
* @var string
1420
*/
15-
private $usernameOrToken;
21+
private $authenticationType;
1622

1723
/**
1824
* @var string
1925
*/
20-
private $password;
26+
private $credentials;
2127

2228
/**
2329
* @param HttpClient $httpClient
24-
* @param string $usernameOrToken
25-
* @param string $password
30+
* @param string $authenticationType
31+
* @param string $credentials
2632
*/
27-
public function __construct(HttpClient $httpClient, $usernameOrToken, $password = null)
33+
public function __construct(HttpClient $httpClient, string $authenticationType, string $credentials)
2834
{
2935
$this->httpClient = $httpClient;
30-
$this->usernameOrToken = $usernameOrToken;
31-
$this->password = $password;
36+
$this->authenticationType = $authenticationType;
37+
$this->credentials = $credentials;
3238
}
3339

3440
/**
3541
* {@inheritdoc}
3642
*/
3743
public function request($method, $path, $body = null, array $options = [])
3844
{
39-
return $this->httpClient->request($method, $path, $body, $this->addAuthenticationHeader($options));
45+
return $this->httpClient->request($method, $path, $body, $this->addAuthenticationOptions($options));
4046
}
4147

4248
/**
4349
* {@inheritdoc}
4450
*/
4551
public function asyncRequest($method, $path, $body = null, array $options = [])
4652
{
47-
return $this->httpClient->asyncRequest($method, $path, $body, $this->addAuthenticationHeader($options));
53+
return $this->httpClient->asyncRequest($method, $path, $body, $this->addAuthenticationOptions($options));
4854
}
4955

5056
/**
5157
* @return string
5258
*/
5359
private function getBasicAuthorizationString()
5460
{
55-
return 'Basic '.base64_encode(sprintf('%s:%s', $this->usernameOrToken, $this->password));
61+
return 'Basic '.base64_encode($this->credentials);
5662
}
5763

5864
/**
5965
* @return string
6066
*/
6167
private function getTokenAuthorizationString()
6268
{
63-
return 'Bearer '.$this->usernameOrToken;
69+
return 'Bearer '.$this->credentials;
6470
}
6571

6672
/**
6773
* @return bool
6874
*/
6975
private function isTokenAuthentication()
7076
{
71-
return null === $this->password;
77+
return self::TOKEN == $this->authenticationType;
7278
}
7379

74-
private function addAuthenticationHeader(array $options): array
80+
private function addAuthenticationOptions(array $options): array
7581
{
76-
$authorizationHeader = $this->isTokenAuthentication() ? $this->getTokenAuthorizationString() : $this->getBasicAuthorizationString();
82+
if (self::CERTIFICATE == $this->authenticationType) {
83+
$authorizationOptions = [
84+
'cert' => certificate_file_path_from_contents($this->credentials),
85+
];
86+
} else {
87+
$authorizationOptions = [
88+
'headers' => [
89+
'Authorization' => $this->isTokenAuthentication() ? $this->getTokenAuthorizationString() : $this->getBasicAuthorizationString(),
90+
],
91+
];
92+
}
7793

78-
return array_merge_recursive([
79-
'headers' => [
80-
'Authorization' => $authorizationHeader,
81-
],
82-
], $options);
94+
return array_merge_recursive($authorizationOptions, $options);
8395
}
8496
}

src/Adapter/Http/GuzzleHttpClient.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use GuzzleHttp\ClientInterface;
66
use Psr\Http\Message\ResponseInterface;
77

8+
require_once 'functions.php';
9+
810
class GuzzleHttpClient implements HttpClient
911
{
1012
/**
@@ -22,16 +24,23 @@ class GuzzleHttpClient implements HttpClient
2224
*/
2325
private $version;
2426

27+
/**
28+
* @var string
29+
*/
30+
private $caCertificate;
31+
2532
/**
2633
* @param ClientInterface $guzzleClient
27-
* @param string $baseUrl
28-
* @param string $version
34+
* @param string $baseUrl
35+
* @param string $version
36+
* @param string $caCertificate
2937
*/
30-
public function __construct(ClientInterface $guzzleClient, $baseUrl, $version)
38+
public function __construct(ClientInterface $guzzleClient, string $baseUrl, string $version, string $caCertificate = null)
3139
{
3240
$this->guzzleClient = $guzzleClient;
3341
$this->baseUrl = $baseUrl;
3442
$this->version = $version;
43+
$this->caCertificate = $caCertificate;
3544
}
3645

3746
/**
@@ -106,6 +115,10 @@ private function prepareOptions($body, $options)
106115
],
107116
];
108117

118+
if (null !== $this->caCertificate) {
119+
$defaults['verify'] = certificate_file_path_from_contents($this->caCertificate);
120+
}
121+
109122
if ($body !== null) {
110123
$defaults['body'] = $body;
111124
}

src/Adapter/Http/functions.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Kubernetes\Client\Adapter\Http;
4+
5+
function certificate_file_path_from_contents(string $certificateContents)
6+
{
7+
$file = tempnam(sys_get_temp_dir(), 'certificate');
8+
file_put_contents($file, $certificateContents);
9+
10+
register_shutdown_function(function() use($file) {
11+
unlink($file);
12+
});
13+
14+
return $file;
15+
}

0 commit comments

Comments
 (0)