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

Parse Region from Secret if User does not provide Region #134

Merged
merged 4 commits into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion docs/using-the-aws-driver/UsingTheAwsDriver.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ 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`
| `AWS_REGION` | Region of the secret. | char* | Optional when secret id is ARN | `us-east-1`
| `SECRET_ID` | Secret name or secret ARN. | char* | No | 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__ */