Skip to content

Commit a057a19

Browse files
committed
Added certs
1 parent cfa2f8e commit a057a19

7 files changed

Lines changed: 229 additions & 2 deletions

File tree

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,10 @@
4747
"classmap": [
4848
"src/"
4949
]
50+
},
51+
"config": {
52+
"allow-plugins": {
53+
"dealerdirect/phpcodesniffer-composer-installer": true
54+
}
5055
}
5156
}

src/Fapi/HttpClient/CurlHttpClient.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
use const CURLOPT_RETURNTRANSFER;
3939
use const CURLOPT_SSL_VERIFYHOST;
4040
use const CURLOPT_SSL_VERIFYPEER;
41+
use const CURLOPT_SSLCERT;
42+
use const CURLOPT_SSLCERTPASSWD;
43+
use const CURLOPT_SSLKEY;
44+
use const CURLOPT_SSLKEYPASSWD;
4145
use const CURLOPT_TIMEOUT;
4246
use const CURLOPT_URL;
4347

@@ -115,10 +119,28 @@ private function processOptions(RequestInterface $request, $handle): void
115119
{
116120
if ($request->hasHeader('timeout')) {
117121
curl_setopt($handle, CURLOPT_TIMEOUT, (int) $request->getHeaderLine('timeout'));
118-
} elseif ($request->hasHeader('connect_timeout')) {
122+
}
123+
124+
if ($request->hasHeader('connect_timeout')) {
119125
curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, (int) $request->getHeaderLine('connect_timeout'));
120126
}
121127

128+
if ($request->hasHeader('cert')) {
129+
curl_setopt($handle, CURLOPT_SSLCERT, $request->getHeader('cert')[0]);
130+
131+
if ($request->getHeader('cert')[1] ?? false) {
132+
curl_setopt($handle, CURLOPT_SSLCERTPASSWD, $request->getHeader('cert')[1]);
133+
}
134+
}
135+
136+
if ($request->hasHeader('ssl_key')) {
137+
curl_setopt($handle, CURLOPT_SSLKEY, $request->getHeader('ssl_key')[0]);
138+
139+
if ($request->getHeader('ssl_key')[1] ?? false) {
140+
curl_setopt($handle, CURLOPT_SSLKEYPASSWD, $request->getHeader('ssl_key')[1]);
141+
}
142+
}
143+
122144
$this->processVerifyOption($request->getHeaderLine('verify'), $handle);
123145
}
124146

@@ -150,7 +172,9 @@ private function processHeaders(RequestInterface $request, $handle): RequestInte
150172
{
151173
$request = $request->withoutHeader('timeout')
152174
->withoutHeader('connect_timeout')
153-
->withoutHeader('verify');
175+
->withoutHeader('verify')
176+
->withoutHeader('cert')
177+
->withoutHeader('ssl_key');
154178

155179
curl_setopt($handle, CURLOPT_HTTPHEADER, $this->formatHeaders($request->getHeaders()));
156180

src/Fapi/HttpClient/GuzzleHttpClient.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use function defined;
1515
use function strlen;
1616
use function strncmp;
17+
use function var_dump;
1718
use const CURLE_OPERATION_TIMEOUTED;
1819

1920
class GuzzleHttpClient implements IHttpClient
@@ -37,6 +38,8 @@ public function sendRequest(RequestInterface $request): ResponseInterface
3738
$request = $request->withoutHeader('timeout')
3839
->withoutHeader('connect_timeout')
3940
->withoutHeader('verify')
41+
->withoutHeader('cert')
42+
->withoutHeader('ssl_key')
4043
->withHeader('Accept-Encoding', 'gzip');
4144

4245
try {
@@ -105,6 +108,15 @@ private function processOptions(RequestInterface $request): array
105108
$options['verify'] = false;
106109
}
107110

111+
var_dump($request->getHeaderLine('cert'));
112+
if ($request->hasHeader('cert')) {
113+
$options['cert'] = $request->getHeaderLine('cert');
114+
}
115+
116+
if ($request->hasHeader('ssl_key')) {
117+
$options['ssl_key'] = $request->getHeaderLine('ssl_key');
118+
}
119+
108120
return $options;
109121
}
110122

src/Fapi/HttpClient/HttpRequest.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
use Psr\Http\Message\UriInterface;
1010
use function base64_encode;
1111
use function count;
12+
use function file_exists;
1213
use function http_build_query;
1314
use function is_array;
1415
use function is_bool;
1516
use function is_int;
1617
use function is_string;
18+
use function json_encode;
19+
use function var_dump;
1720

1821
class HttpRequest extends Request
1922
{
@@ -106,6 +109,18 @@ private static function preProcessHeaders(array $options, &$body): array
106109
$data['verify'] = (bool) $value;
107110
}
108111

112+
if (isset($options['cert'])) {
113+
$value = $options['cert'];
114+
static::validateCertOption($value);
115+
$data['cert'] = $value;
116+
}
117+
118+
if (isset($options['ssl_key'])) {
119+
$value = $options['ssl_key'];
120+
static::validateSslKeyOption($value);
121+
$data['ssl_key'] = $value;
122+
}
123+
109124
return $data;
110125
}
111126

@@ -223,4 +238,76 @@ private static function validateVerify($verify): void
223238
}
224239
}
225240

241+
/**
242+
* @param mixed $value
243+
*/
244+
private static function validateCertOption($value): void
245+
{
246+
if (!is_string($value) && !is_array($value)) {
247+
throw new InvalidArgumentException('Option cert must be a string.');
248+
}
249+
250+
if (is_array($value)) {
251+
if (!isset($value[0]) || !isset($value[1])) {
252+
throw new InvalidArgumentException(
253+
'Option cert must be an array of two elements (cert and key). Provided array: ' . json_encode(
254+
$value
255+
)
256+
);
257+
}
258+
259+
if (!is_string($value[0]) || !is_string($value[1])) {
260+
throw new InvalidArgumentException(
261+
'Option cert must be an array of two strings (cert and key). Provided array: ' . json_encode($value)
262+
);
263+
}
264+
265+
$file = $value[0];
266+
} else {
267+
$file = $value;
268+
}
269+
270+
var_dump(file_exists($file));
271+
272+
if (!file_exists($file)) {
273+
throw new InvalidArgumentException('Option cert file not found. Provided file: ' . $file);
274+
}
275+
}
276+
277+
/**
278+
* @param mixed $value
279+
*/
280+
private static function validateSslKeyOption($value): void
281+
{
282+
if (!is_string($value) && !is_array($value)) {
283+
throw new InvalidArgumentException('Option ssl_key must be a string.');
284+
}
285+
286+
if (is_array($value)) {
287+
if (!isset($value[0]) || !isset($value[1])) {
288+
throw new InvalidArgumentException(
289+
'Option ssl_key must be an array of two elements (key and password). Provided array: ' . json_encode(
290+
$value
291+
) . '.'
292+
);
293+
}
294+
295+
if (!is_string($value[0]) || !is_string($value[1])) {
296+
throw new InvalidArgumentException(
297+
'Option ssl_key must be an array of two strings (key and password). Provided array: ' . json_encode(
298+
$value
299+
) . '.'
300+
);
301+
}
302+
303+
$file = $value[0];
304+
} else {
305+
$file = $value;
306+
}
307+
308+
if (!file_exists($file)) {
309+
throw new InvalidArgumentException('Option ssl_key file not found. Provided file: ' . $file);
310+
}
311+
}
312+
226313
}

tests/Fapi/HttpClientTests/HttpRequestTest.phpt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ final class HttpRequestTest extends TestCase
106106
'connect_timeout' => 5,
107107
],
108108
],
109+
[
110+
'headers' => [
111+
'cert' => __DIR__ . '/certs/private-key.key',
112+
],
113+
],
114+
[
115+
'headers' => [
116+
'ssl_key' => __DIR__ . '/certs/public-key.pem',
117+
],
118+
],
109119
];
110120
}
111121

@@ -175,6 +185,36 @@ final class HttpRequestTest extends TestCase
175185
'connect_timeout' => 5.0,
176186
],
177187
],
188+
[
189+
'headers' => [
190+
'cert' => 5,
191+
],
192+
],
193+
[
194+
'headers' => [
195+
'ssl_key' => 5,
196+
],
197+
],
198+
[
199+
'headers' => [
200+
'cert' => [1],
201+
],
202+
],
203+
[
204+
'headers' => [
205+
'ssl_key' => [1],
206+
],
207+
],
208+
[
209+
'headers' => [
210+
'cert' => [1, 1],
211+
],
212+
],
213+
[
214+
'headers' => [
215+
'ssl_key' => [1, 1],
216+
],
217+
],
178218
];
179219
}
180220

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcr/oxgV074ETd
3+
DkP/0l8LFnRofru+m2wNNG/ttVCioTqwnvR4oYxwq3U9qIBsT0D+Rx/Ef7qcpzqf
4+
/w9xt6Hosdv6I5jMHGaVQqLiPuV26/a7WvcmU+PpYuEBmbBHjGVJRBwgPtlUW1VL
5+
M8Pht9YiaagEKvFa6SUidZLfPv+ECohqgH4mgMrEcG/BTnry0/5xQdadRC9o25cl
6+
NtZIesS5GPeelhggFTkbh/FaxvMXhIAaRXT61cnxgxtfM71h5ObX5Lwle9z5a+Tw
7+
xgQhSQq1jbHALYvTwsc4Q/NQGXpGNWy599sb7dg5AkPFSSF4ceXBo/2jOaZCqWrt
8+
FVONZ+blAgMBAAECggEBAJwQbrRXsaFIRiq1jez5znC+3m+PQCHZM55a+NR3pqB7
9+
uE9y+ZvdUr3S4sRJxxfRLDsl/Rcu5L8nm9PNwhQ/MmamcNQCHGoro3fmed3ZcNia
10+
og94ktMt/DztygUhtIHEjVQ0sFc1WufG9xiJcPrM0MfhRAo+fBQ4UCSAVO8/U98B
11+
a4yukrPNeEA03hyjLB9W41pNQfyOtAHqzwDg9Q5XVaGMCLZT1bjCIquUcht5iMva
12+
tiw3cwdiYIklLTzTCsPPK9A/AlWZyUXL8KxtN0mU0kkwlXqASoXZ2nqdkhjRye/V
13+
3JXOmlDtDaJCqWDpH2gHLxMCl7OjfPvuD66bAT3H63kCgYEA5zxW/l6oI3gwYW7+
14+
j6rEjA2n8LikVnyW2e/PZ7pxBH3iBFe2DHx/imeqd/0IzixcM1zZT/V+PTFPQizG
15+
lOU7stN6Zg/LuRdxneHPyLWCimJP7BBJCWyJkuxKy9psokyBhGSLR/phL3fP7UkB
16+
o2I3vGmTFu5A0FzXcNH/cXPMdy8CgYEA9FJw3kyzXlInhJ6Cd63mckLPLYDArUsm
17+
THBoeH2CVTBS5g0bCbl7N1ZxUoYwZPD4lg5V0nWhZALGf+85ULSjX03PMf1cc6WW
18+
EIbZIo9hX+mGRa/FudDd+TlbtBnn0jucwABuLQi9mIepE55Hu9tw5/FT3cHeZVQc
19+
cC0T6ulVvisCgYBCzFeFG+sOdAXl356B+h7VJozBKVWv9kXNp00O9fj4BzVnc78P
20+
VFezr8a66snEZWQtIkFUq+JP4xK2VyD2mlHoktbk7OM5EOCtbzILFQQk3cmgtAOl
21+
SUlkvAXPZcXEDL3NdQ4XOOkiQUY7kb97Z0AamZT4JtNqXaeO29si9wS12QKBgHYg
22+
Hd3864Qg6GZgVOgUNiTsVErFw2KFwQCYIIqQ9CDH+myrzXTILuC0dJnXszI6p5W1
23+
XJ0irmMyTFKykN2KWKrNbe3Xd4mad5GKARWKiSPcPkUXFNwgNhI3PzU2iTTGCaVz
24+
D9HKNhC3FnIbxsb29AHQViITh7kqD43U3ZpoMkJ9AoGAZ+sg+CPfuo3ZMpbcdb3B
25+
ZX2UhAvNKxgHvNnHOjO+pvaM7HiH+BT0650brfBWQ0nTG1dt18mCevVk1UM/5hO9
26+
AtZw06vCLOJ3p3qpgkSlRZ1H7VokG9M8Od0zXqtJrmeLeBq7dfuDisYOuA+NUEbJ
27+
UM/UHByieS6ywetruz0LpM0=
28+
-----END RSA PRIVATE KEY-----
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIFSTCCAzGgAwIBAgIEAQIDBDANBgkqhkiG9w0BAQsFADCBgDELMAkGA1UEBhMC
3+
Q1oxDjAMBgNVBAcTBUN6ZWNoMRMwEQYDVQQKEwpFcnN0ZUdyb3VwMRUwEwYDVQQL
4+
EwxFcnN0ZUh1YlRlYW0xETAPBgNVBAMTCEVyc3RlSHViMSIwIAYJKoZIhvcNAQkB
5+
FhNpbmZvQGVyc3RlZ3JvdXAuY29tMB4XDTIyMTIxNDA4MDc1N1oXDTI2MDMxNDA4
6+
MDc1N1owUjEaMBgGA1UEYRMRUFNEQ1otQ05CLTEyMzQ1NjcxCzAJBgNVBAYTAkNa
7+
MRYwFAYDVQQDEw1UUFAgVGVzdCBRV0FDMQ8wDQYDVQQKEwZNeSBUUFAwggEiMA0G
8+
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcr/oxgV074ETdDkP/0l8LFnRofru+
9+
m2wNNG/ttVCioTqwnvR4oYxwq3U9qIBsT0D+Rx/Ef7qcpzqf/w9xt6Hosdv6I5jM
10+
HGaVQqLiPuV26/a7WvcmU+PpYuEBmbBHjGVJRBwgPtlUW1VLM8Pht9YiaagEKvFa
11+
6SUidZLfPv+ECohqgH4mgMrEcG/BTnry0/5xQdadRC9o25clNtZIesS5GPeelhgg
12+
FTkbh/FaxvMXhIAaRXT61cnxgxtfM71h5ObX5Lwle9z5a+TwxgQhSQq1jbHALYvT
13+
wsc4Q/NQGXpGNWy599sb7dg5AkPFSSF4ceXBo/2jOaZCqWrtFVONZ+blAgMBAAGj
14+
gfcwgfQwCwYDVR0PBAQDAgHGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
15+
AjCBrwYIKwYBBQUHAQMEgaIwgZ8wCAYGBACORgEBMAsGBgQAjkYBAwIBFDAIBgYE
16+
AI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgMwZwYGBACBmCcCMF0wTDARBgcEAIGY
17+
JwEBDAZQU1BfQVMwEQYHBACBmCcBAgwGUFNQX1BJMBEGBwQAgZgnAQMMBlBTUF9B
18+
STARBgcEAIGYJwEEDAZQU1BfSUMMBUVyc3RlDAZBVC1FUlMwFAYDVR0RBA0wC4IJ
19+
bXl0cHAuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBlTMPSwz46GMRBEPcy+25gV7xE
20+
5aFS5N6sf3YQyFelRJgPxxPxTHo55WelcK4XmXRQKeQ4VoKf4FgP0Cj74+p0N0gw
21+
wFJDWPGXH3SdjAXPRtG+FOiHwUSoyrmvbL4kk6Vbrd4cF+qe0BlzHzJ2Q6vFLwsk
22+
NYvWzkY9YjoItB38nAnQhyYgl1yHUK/uDWyrwHVfZn1AeTws/hr/KufORuiQfaTU
23+
kvAH1nzi7WSJ6AIQCd2exUEPx/O14Y+oCoJhTVd+RpA/9lkcqebceBijj47b2bvv
24+
QbjymvyTXqHd3L224Y7zVmh95g+CaJ8PRpApdrImfjfDDRy8PaFWx2pd/v0UQgrQ
25+
lgbO6jE7ah/tS0T5q5JtwnLAiOOqHPaKRvo5WB65jcZ2fvOH/0/oZ89noxp1Ihus
26+
vvsjqc9k2h9Rvt2pEjVU40HtQZ6XCmWqgFwK3n9CHrKNV/GqgANIZRNcvXKMCUoB
27+
VoJORVwi2DF4caKSFmyEWuK+5FyCEILtQ60SY/NHVGsUeOuN7OTjZjECARO6p4hz
28+
Uw+GCIXrzmIjS6ydh/LRef+NK28+xTbjmLHu/wnHg9rrHEnTPd39is+byfS7eeLV
29+
Dld/0Xrv88C0wxz63dcwAceiahjyz2mbQm765tOf9rK7EqsvT5M8EXFJ3dP4zwqS
30+
6mNFoIa0XGbAUT3E1w==
31+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)