Skip to content

Commit

Permalink
Parse Region from Secret if User does not provide Region (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
justing-bq authored and yanw-bq committed May 2, 2023
1 parent 8ede0f6 commit f734eab
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 9 deletions.
10 changes: 5 additions & 5 deletions docs/using-the-aws-driver/UsingTheAwsDriver.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,9 @@ IAM database authentication use is limited to certain database engines. For more

| Option | Description | Type | Required | Default |
| ---------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|------------------------------------------|
| `AUTHENTICATION_MODE` | Set to `IAM` to enable IAM Authentication. | char* | No | Off |
| `AUTHENTICATION_MODE` | Set to `IAM` to enable IAM Authentication. | char* | Yes | Off |
| `AWS_REGION` | Region used to generate IAM tokens. | char* | No | `us-east-1`
| `IAM_HOST` | Host URL for IAM authentication. URL must be a valid Amazon endpoint, and not a custom domain or an IP address. | char* | No | Empty |
| `IAM_HOST` | Host URL for IAM authentication. URL must be a valid Amazon endpoint, and not a custom domain or an IP address. | char* | Yes | Empty |
| `IAM_PORT` | Port used for IAM authentication. | int | No | `3306` |
| `IAM_EXPIRATION_TIME` | Amount of time in seconds before the generated IAM token expires. After expiration, future connections will require a new token to be generated. | int | No | `900`

Expand All @@ -330,9 +330,9 @@ The following parameters are required for the AWS Secrets Manager Connection Plu

| Option | Description | Type | Required | Default |
| ---------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|------------------------------------------|
| `AUTHENTICATION_MODE` | Set to `SECRETS MANAGER` to enable Secrets Manager Authentication. | char* | No | Off |
| `AWS_REGION` | Region of the secret. | char* | No | `us-east-1`
| `SECRET_ID` | Secret name or secret ARN. | char* | No | Empty
| `AUTHENTICATION_MODE` | Set to `SECRETS MANAGER` to enable Secrets Manager Authentication. | char* | Yes | Off |
| `AWS_REGION` | Region of the secret. | char* | Optional when secret id is ARN | `us-east-1`
| `SECRET_ID` | Secret name or secret ARN. | char* | Yes | Empty

If you are working with the Windows DSN UI, click `Details >>` and navigate to the `AWS Authentication` tab to configure the parameters.

Expand Down
30 changes: 27 additions & 3 deletions driver/secrets_manager_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include <aws/secretsmanager/SecretsManagerServiceClientModel.h>
#include <aws/secretsmanager/model/GetSecretValueRequest.h>
#include <regex>

#include "aws_sdk_helper.h"
#include "secrets_manager_proxy.h"
Expand All @@ -43,6 +44,7 @@ namespace {
AWS_SDK_HELPER SDK_HELPER;
const Aws::String USERNAME_KEY{ "username" };
const Aws::String PASSWORD_KEY{ "password" };
const std::string SECRETS_ARN_PATTERN{ "arn:aws:secretsmanager:([-a-zA-Z0-9]+):.*" };
}

std::map<std::pair<Aws::String, Aws::String>, Aws::Utils::Json::JsonValue> SECRETS_MANAGER_PROXY::secrets_cache;
Expand All @@ -51,12 +53,23 @@ std::mutex SECRETS_MANAGER_PROXY::secrets_cache_mutex;
SECRETS_MANAGER_PROXY::SECRETS_MANAGER_PROXY(DBC* dbc, DataSource* ds) : CONNECTION_PROXY(dbc, ds) {
++SDK_HELPER;

const char* secret_ID = nullptr;
std::string region;
if (ds->auth_secret_id) {
secret_ID = ds_get_utf8attr(ds->auth_secret_id, &ds->auth_secret_id8);
}
if (ds->auth_region) {
region = ds_get_utf8attr(ds->auth_region, &ds->auth_region8);
}

if (secret_ID && region.empty()) {
try_parse_region_from_secret(secret_ID, region);
}

SecretsManagerClientConfiguration config;
const auto region = ds_get_utf8attr(ds->auth_region, &ds->auth_region8);
config.region = region ? region : Aws::Region::US_EAST_1;
config.region = !region.empty() ? region : Aws::Region::US_EAST_1;
this->sm_client = std::make_shared<SecretsManagerClient>(config);

const auto secret_ID = ds_get_utf8attr(ds->auth_secret_id, &ds->auth_secret_id8);
this->secret_key = std::make_pair(secret_ID ? secret_ID : "", config.region);
this->next_proxy = nullptr;
}
Expand Down Expand Up @@ -179,3 +192,14 @@ std::string SECRETS_MANAGER_PROXY::get_from_secret_json_value(std::string key) {
}
return value;
}

bool SECRETS_MANAGER_PROXY::try_parse_region_from_secret(std::string secret, std::string& region) {
std::regex rgx(SECRETS_ARN_PATTERN);
std::smatch matches;
if (std::regex_search(secret, matches, rgx) && matches.size() > 1) {
region = matches[1].str();
return true;
}

return false;
}
1 change: 1 addition & 0 deletions driver/secrets_manager_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class SECRETS_MANAGER_PROXY : public CONNECTION_PROXY {
bool fetch_latest_credentials();
bool parse_json_value(Aws::String json_string);
std::string get_from_secret_json_value(std::string key);
static bool try_parse_region_from_secret(std::string secret, std::string& region);

static std::map<std::pair<Aws::String, Aws::String>, Aws::Utils::Json::JsonValue> secrets_cache;
static std::mutex secrets_cache_mutex;
Expand Down
16 changes: 15 additions & 1 deletion integration/secrets_manager_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class SecretsManagerIntegrationTest : public testing::Test {
}
};

TEST_F(SecretsManagerIntegrationTest, EnableSecretsManager) {
TEST_F(SecretsManagerIntegrationTest, EnableSecretsManagerWithRegion) {
connection_string = builder
.withDSN(dsn)
.withServer(MYSQL_CLUSTER_URL)
Expand All @@ -98,6 +98,20 @@ TEST_F(SecretsManagerIntegrationTest, EnableSecretsManager) {
EXPECT_EQ(SQL_SUCCESS, SQLDisconnect(dbc));
}

TEST_F(SecretsManagerIntegrationTest, EnableSecretsManagerWithoutRegion) {
connection_string = builder
.withDSN(dsn)
.withServer(MYSQL_CLUSTER_URL)
.withAuthMode("SECRETS MANAGER")
.withSecretId(SECRETS_ARN)
.build();
SQLCHAR conn_out[4096] = "\0";
SQLSMALLINT len;

EXPECT_EQ(SQL_SUCCESS, SQLDriverConnect(dbc, nullptr, AS_SQLCHAR(connection_string.c_str()), SQL_NTS, conn_out, MAX_NAME_LEN, &len, SQL_DRIVER_NOPROMPT));
EXPECT_EQ(SQL_SUCCESS, SQLDisconnect(dbc));
}

TEST_F(SecretsManagerIntegrationTest, EnableSecretsManagerWrongRegion) {
connection_string = builder
.withDSN(dsn)
Expand Down
19 changes: 19 additions & 0 deletions unit_testing/secrets_manager_proxy_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,22 @@ TEST_F(SecretsManagerProxyTest, TestFailedToGetSecrets) {
EXPECT_FALSE(ret);
EXPECT_EQ(0, TEST_UTILS::get_secrets_cache().size());
}

TEST_F(SecretsManagerProxyTest, ParseRegionFromSecret) {
std::string region = "";
EXPECT_TRUE(TEST_UTILS::try_parse_region_from_secret(
"arn:aws:secretsmanager:us-east-1:123456789012:secret:MyTestDatabaseSecret-a1b2c3", region));
EXPECT_EQ("us-east-1", region);

region = "";
EXPECT_TRUE(TEST_UTILS::try_parse_region_from_secret(
"arn:aws:secretsmanager:us-west-3:0987654321:Whatever", region));
EXPECT_EQ("us-west-3", region);

region = "";
EXPECT_FALSE(TEST_UTILS::try_parse_region_from_secret(
"arn:aws:secretmanager:us-east-1:123456789012:secret:MyTestDatabaseSecret-a1b2c3", region));
EXPECT_EQ("", region);

delete mock_connection_proxy;
}
4 changes: 4 additions & 0 deletions unit_testing/test_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,7 @@ void TEST_UTILS::clear_token_cache(IAM_PROXY* iam_proxy) {
std::map<std::pair<Aws::String, Aws::String>, Aws::Utils::Json::JsonValue>& TEST_UTILS::get_secrets_cache() {
return std::ref(SECRETS_MANAGER_PROXY::secrets_cache);
}

bool TEST_UTILS::try_parse_region_from_secret(std::string secret, std::string& region) {
return SECRETS_MANAGER_PROXY::try_parse_region_from_secret(secret, region);
}
1 change: 1 addition & 0 deletions unit_testing/test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class TEST_UTILS {
static bool token_cache_contains_key(std::string cache_key);
static void clear_token_cache(IAM_PROXY* iam_proxy);
static std::map<std::pair<Aws::String, Aws::String>, Aws::Utils::Json::JsonValue>& get_secrets_cache();
static bool try_parse_region_from_secret(std::string secret, std::string& region);
};

#endif /* __TESTUTILS_H__ */

0 comments on commit f734eab

Please sign in to comment.