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

dev to main #64

Merged
merged 4 commits into from
Nov 22, 2023
Merged
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
4 changes: 3 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ jobs:
- '7.3'
- '7.4'
- '8.0'
- '8.1'
- '8.2'
steps:
- name: Checkout code
uses: actions/checkout@v2
Expand Down Expand Up @@ -64,4 +66,4 @@ jobs:
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
coverage-reports: tests/_output/coverage.xml


3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"psr/cache": "^1.0|^2.0|^3.0",
"psr/log": "^1.0|^2.0|^3.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0"
"psr/http-factory": "^1.0",
"aws/aws-sdk-php": "^3.0"
},
"require-dev": {
"codeception/codeception": "^4.1",
Expand Down
120 changes: 120 additions & 0 deletions src/AuthenticationStrategies/AwsIamAuthenticationStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

namespace Vault\AuthenticationStrategies;

use Aws\Middleware;
use Aws\Sts\StsClient;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Client\ClientExceptionInterface;
use Vault\Exceptions\AuthenticationException;
use Vault\ResponseModels\Auth;

/**
* Class AwsIamAuthenticationStrategy
*
* @package Vault\AuthenticationStrategy
*/
class AwsIamAuthenticationStrategy extends AbstractAuthenticationStrategy
{
/**
* @var string
*/
protected $methodPathSegment = 'aws';

/** @var string */
private $role;

/** @var string */
private $region;

/** @var ?string */
private $serverId;

/**
* @param string $role The name of the vault role
* @param string $region The AWS region to use
* @param ?string $vaultServerId If set, this string is used as X-Vault-AWS-IAM-Server-ID, to protect against replay attacks
* @param ?StsClient $stsClient Custom instance of StsClient
*/
public function __construct(
$role,
$region,
$vaultServerId = null,
$stsClient = null
) {
$this->role = $role;
$this->region = $region;
$this->serverId = $vaultServerId;
$this->stsClient = $stsClient;
}

/**
* Returns auth for further interactions with Vault.
*
* @return Auth
* @throws AuthenticationException
* @throws ClientExceptionInterface
*/
public function authenticate(): Auth
{
if (!$this->methodPathSegment) {
throw new AuthenticationException('methodPathSegment must be set before usage');
}

if (!$this->stsClient) {
$this->stsClient = new StsClient([
'region' => $this->region,
'version' => 'latest',
'sts_regional_endpoints' => 'regional',
]);
}


// Creating a signed command, to get the parameters for the actual login-request to vault
$command = $this->stsClient->getCommand('GetCallerIdentity');

if ($this->serverId) {
$command->getHandlerList()->appendBuild(
Middleware::mapRequest(function (RequestInterface $request) {
return $request->withHeader('X-Vault-AWS-IAM-Server-ID', $this->serverId);
}),
'add-header'
);
}

$request = \Aws\serialize($command);

$response = $this->client->write(
sprintf('/auth/%s/login', $this->methodPathSegment),
[
'role' => $this->role,
'iam_http_request_method' => $request->getMethod(),
'iam_request_url' => base64_encode($request->getUri()),
'iam_request_body' => base64_encode($request->getBody()),
'iam_request_headers' => base64_encode(json_encode($request->getHeaders())),
]
);

return $response->getAuth();
}

/**
* @return string
*/
public function getMethodPathSegment(): string
{
return $this->methodPathSegment;
}

/**
* @param string $methodPathSegment
*
* @return static
*/
public function setMethodPathSegment(string $methodPathSegment)
{
$this->methodPathSegment = $methodPathSegment;

return $this;
}
}
6 changes: 3 additions & 3 deletions src/ResponseModels/Traits/LeaseTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
trait LeaseTrait
{
/**
* @var int|null
* @var string|null
*/
protected $leaseId;

Expand All @@ -25,9 +25,9 @@ trait LeaseTrait
protected $renewable;

/**
* @return int|null
* @return string|null
*/
public function getLeaseId(): ?int
public function getLeaseId(): ?string
{
return $this->leaseId;
}
Expand Down
46 changes: 46 additions & 0 deletions tests/_data/vcr/authentication-strategies/aws-iam
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

-
request:
method: POST
url: 'http://127.0.0.1:8200/v1/auth/aws/login'
headers:
Host: '127.0.0.1:8200'
Expect: null
Accept-Encoding: null
User-Agent: VaultPHP/1.0.0
Content-Type: application/json
Accept: null
body: '{"role":"dev-role","iam_http_request_method":"POST","iam_request_url":"aHR0cHM6Ly9zdHMuZXUtY2VudHJhbC0xLmFtYXpvbmF3cy5jb20=","iam_request_body":"QWN0aW9uPUdldENhbGxlcklkZW50aXR5JlZlcnNpb249MjAxMS0wNi0xNQ==","iam_request_headers":"eyJIb3N0IjpbInN0cy5ldS1jZW50cmFsLTEuYW1hem9uYXdzLmNvbSJdLCJDb250ZW50LUxlbmd0aCI6WyI0MyJdLCJDb250ZW50LVR5cGUiOlsiYXBwbGljYXRpb25cL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCJdLCJYLUFtei1Vc2VyLUFnZW50IjpbImF3cy1zZGstcGhwXC8zLjI2MC4zIE9TXC9EYXJ3aW5cLzIyLjEuMCBsYW5nXC9waHBcLzguMC4yNyJdLCJVc2VyLUFnZW50IjpbImF3cy1zZGstcGhwXC8zLjI2MC4zIE9TXC9EYXJ3aW5cLzIyLjEuMCBsYW5nXC9waHBcLzguMC4yNyJdLCJYLVZhdWx0LUFXUy1JQU0tU2VydmVyLUlEIjpbImxvY2FsaG9zdCJdLCJhd3Mtc2RrLXJldHJ5IjpbIjBcLzAiXX0="}'
response:
status:
http_version: '1.1'
code: '200'
message: OK
headers:
Cache-Control: no-store
Content-Type: application/json
Date: 'Tue, 01 Aug 2017 06:54:33 GMT'
Content-Length: '366'
body: "{\"request_id\":\"08962062-3aab-fd89-3413-99c3521ecc75\",\"lease_id\":\"\",\"renewable\":false,\"lease_duration\":0,\"data\":null,\"wrap_info\":null,\"warnings\":null,\"auth\":{\"client_token\":\"9e64c3b8-01f7-7a64-1575-30a91f7d1ae4\",\"accessor\":\"033ccada-d332-9415-0acc-0ec51e81dbd1\",\"policies\":[\"default\",\"test\"],\"metadata\":{\"username\":\"test\"},\"lease_duration\":2764800,\"renewable\":true}}\n"
-
request:
method: GET
url: 'http://127.0.0.1:8200/v1/auth/token/lookup-self'
headers:
Host: '127.0.0.1:8200'
Accept-Encoding: null
User-Agent: VaultPHP/1.0.0
Content-Type: application/json
X-Vault-Token: 9e64c3b8-01f7-7a64-1575-30a91f7d1ae4
Accept: null
response:
status:
http_version: '1.1'
code: '200'
message: OK
headers:
Cache-Control: no-store
Content-Type: application/json
Date: 'Tue, 01 Aug 2017 06:54:33 GMT'
Content-Length: '504'
body: "{\"request_id\":\"ab3f3dc9-3633-0c18-44e2-f3f58cb56d0a\",\"lease_id\":\"\",\"renewable\":false,\"lease_duration\":0,\"data\":{\"accessor\":\"033ccada-d332-9415-0acc-0ec51e81dbd1\",\"creation_time\":1501570473,\"creation_ttl\":2764800,\"display_name\":\"userpass-test\",\"explicit_max_ttl\":0,\"id\":\"9e64c3b8-01f7-7a64-1575-30a91f7d1ae4\",\"meta\":{\"username\":\"test\"},\"num_uses\":0,\"orphan\":true,\"path\":\"auth/userpass/login/test\",\"policies\":[\"default\",\"test\"],\"renewable\":true,\"ttl\":2764800},\"wrap_info\":null,\"warnings\":null,\"auth\":null}\n"
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

use Aws\Sts\StsClient;
use Codeception\Test\Unit;
use Psr\Http\Client\ClientExceptionInterface;
use Vault\AuthenticationStrategies\AwsIamAuthenticationStrategy;
use Vault\Client;
use VCR\VCR;
use Laminas\Diactoros\RequestFactory;
use Laminas\Diactoros\StreamFactory;
use Laminas\Diactoros\Uri;

class AwsIamAuthenticationStrategyTest extends Unit
{
/**
* @var UnitTester
*/
protected $tester;

/**
* @throws \Psr\Cache\InvalidArgumentException
* @throws ClientExceptionInterface
* @throws \Vault\Exceptions\RuntimeException
*/
public function testCanAuthenticate(): void
{
$client = new Client(
new Uri('http://127.0.0.1:8200'),
new \AlexTartan\GuzzlePsr18Adapter\Client(),
new RequestFactory(),
new StreamFactory()
);

$stsClient = new StsClient([
'region' => 'eu-central-1',
'version' => 'latest',
'sts_regional_endpoints' => 'regional'
]);
// These middlewares would break the test, due to some dynamic headers
$stsClient->getHandlerList()->remove('invocation-id');
$stsClient->getHandlerList()->remove('signer');

$client->setAuthenticationStrategy(
new AwsIamAuthenticationStrategy(
'dev-role',
'eu-central-1',
'localhost',
$stsClient
)
);

$this->assertEquals($client->getAuthenticationStrategy()->getClient(), $client);
$this->assertTrue($client->authenticate());
$this->assertNotEmpty($client->getToken());
$this->assertNotEmpty($client->getToken()->getAuth()->getLeaseDuration());
$this->assertNotEmpty($client->getToken()->getAuth()->isRenewable());
}

protected function setUp(): void
{
VCR::turnOn();
VCR::configure()->setMode(VCR::MODE_ONCE);

VCR::insertCassette('authentication-strategies/aws-iam');

parent::setUp();
}

protected function tearDown(): void
{
// To stop recording requests, eject the cassette
VCR::eject();

// Turn off VCR to stop intercepting requests
VCR::turnOff();

parent::tearDown();
}

protected function _before()
{
}

protected function _after()
{
}
}
Loading