Skip to content

Commit de1e645

Browse files
connorhufabpot
authored andcommitted
added AWS instance credential provider for SES transport (api and http)
1 parent 117acc3 commit de1e645

File tree

9 files changed

+495
-94
lines changed

9 files changed

+495
-94
lines changed

src/Symfony/Component/Mailer/Bridge/Amazon/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ CHANGELOG
55
-----
66

77
* [BC BREAK] Renamed and moved `Symfony\Component\Mailer\Bridge\Amazon\Http\Api\SesTransport`
8-
to `Symfony\Component\Mailer\Bridge\Amazon\Transpor\SesApiTransport`, `Symfony\Component\Mailer\Bridge\Amazon\Http\SesTransport`
8+
to `Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiTransport`, `Symfony\Component\Mailer\Bridge\Amazon\Http\SesTransport`
99
to `Symfony\Component\Mailer\Bridge\Amazon\Transport\SesHttpTransport`, `Symfony\Component\Mailer\Bridge\Amazon\Smtp\SesTransport`
1010
to `Symfony\Component\Mailer\Bridge\Amazon\Transport\SesSmtpTransport`.
11+
* [BC BREAK] changed `Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiTransport::__construct` username and password arguments to credential
12+
* [BC BREAK] changed `Symfony\Component\Mailer\Bridge\Amazon\Transport\SesHttpTransport::__construct` username and password arguments to credential
13+
* [BC BREAK] changed `Symfony\Component\Mailer\Bridge\Amazon\Transport\SesSmtpTransport::__construct` username and password arguments to credential
14+
* Added Instance Profile support
1115

1216
4.3.0
1317
-----
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Amazon\Credential;
13+
14+
/**
15+
* @author Karoly Gossler <connor@connor.hu>
16+
*/
17+
final class ApiTokenCredential
18+
{
19+
private $accessKey;
20+
21+
private $secretKey;
22+
23+
private $token;
24+
25+
private $expiration;
26+
27+
public function __construct(string $accessKey, string $secretKey, string $token, \DateTime $expiration)
28+
{
29+
$this->accessKey = $accessKey;
30+
$this->secretKey = $secretKey;
31+
$this->token = $token;
32+
$this->expiration = $expiration;
33+
}
34+
35+
public function getAccessKey(): string
36+
{
37+
return $this->accessKey;
38+
}
39+
40+
public function getSecretKey(): string
41+
{
42+
return $this->secretKey;
43+
}
44+
45+
public function getToken(): string
46+
{
47+
return $this->token;
48+
}
49+
50+
public function getExpiration(): \DateTime
51+
{
52+
return $this->expiration;
53+
}
54+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Amazon\Credential;
13+
14+
use Symfony\Component\HttpClient\HttpClient;
15+
use Symfony\Component\Mailer\Exception\RuntimeException;
16+
use Symfony\Contracts\HttpClient\HttpClientInterface;
17+
18+
/**
19+
* Based on: aws-sdk-php / Credentials/InstanceProfileProvider.php.
20+
*
21+
* @author Karoly Gossler <connor@connor.hu>
22+
*/
23+
class InstanceCredentialProvider
24+
{
25+
const SERVER_URI_TEMPLATE = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/%role_name%';
26+
27+
public function __construct(HttpClientInterface $client = null, int $retries = 3)
28+
{
29+
$this->retries = $retries;
30+
$this->client = $client;
31+
32+
if (null === $this->client) {
33+
if (!class_exists(HttpClient::class)) {
34+
throw new LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__));
35+
}
36+
37+
$this->client = HttpClient::create();
38+
}
39+
}
40+
41+
public function getCredential(string $roleName): ApiTokenCredential
42+
{
43+
$attempts = 0;
44+
45+
$instanceMetadataServerURL = str_replace('%role_name%', $roleName, self::SERVER_URI_TEMPLATE);
46+
47+
while (true) {
48+
try {
49+
++$attempts;
50+
51+
$response = $this->client->request('GET', $instanceMetadataServerURL);
52+
53+
if (200 === $response->getStatusCode()) {
54+
$content = json_decode($response->getContent(), true);
55+
56+
if (null === $content) {
57+
throw new RuntimeException('Unexpected instance metadata response.');
58+
}
59+
60+
if ('Success' !== $content['Code']) {
61+
$msg = sprintf('Unexpected instance profile response: %s', $content['Code']);
62+
throw new RuntimeException($msg);
63+
}
64+
65+
return new ApiTokenCredential($content['AccessKeyId'], $content['SecretAccessKey'], $content['Token'], new \DateTime($content['Expiration']));
66+
} elseif (404 === $response->getStatusCode()) {
67+
$attempts = $this->retries + 1;
68+
}
69+
70+
sleep(pow(1.2, $attempts));
71+
} catch (\Exception $e) {
72+
}
73+
74+
if ($attempts > $this->retries) {
75+
throw new RuntimeException('Error retrieving credentials from instance metadata server.');
76+
}
77+
}
78+
}
79+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Amazon\Credential;
13+
14+
/**
15+
* @author Karoly Gossler <connor@connor.hu>
16+
*/
17+
final class UsernamePasswordCredential
18+
{
19+
private $username;
20+
21+
private $password;
22+
23+
public function __construct(string $username, string $password)
24+
{
25+
$this->username = $username;
26+
$this->password = $password;
27+
}
28+
29+
public function getUsername(): string
30+
{
31+
return $this->username;
32+
}
33+
34+
public function getPassword(): string
35+
{
36+
return $this->password;
37+
}
38+
}

0 commit comments

Comments
 (0)