@@ -8,10 +8,32 @@ namespace launchdarkly::server_side::integrations::detail {
88
99namespace {
1010
11- bool HasExplicitCredentials (DynamoDBClientOptions const & options) {
12- return options.aws_access_key_id .has_value () ||
13- options.aws_secret_access_key .has_value () ||
14- options.aws_session_token .has_value ();
11+ // Verifies that the credential fields in `options` form a valid combination.
12+ // Returns an empty optional on success, or an error string describing what's
13+ // wrong. The valid combinations are:
14+ //
15+ // - none of the three set (fall through to AWS default credential chain)
16+ // - access_key_id + secret_access_key (long-lived IAM keys)
17+ // - access_key_id + secret_access_key + session_token (STS temporary creds)
18+ //
19+ // All other partial combinations would build a misconfigured AWS client that
20+ // fails opaquely at request time; catching them here surfaces the
21+ // misconfiguration up front.
22+ std::optional<std::string> ValidateCredentials (
23+ DynamoDBClientOptions const & options) {
24+ bool const has_key = options.aws_access_key_id .has_value ();
25+ bool const has_secret = options.aws_secret_access_key .has_value ();
26+ bool const has_token = options.aws_session_token .has_value ();
27+
28+ if (has_key != has_secret) {
29+ return " aws_access_key_id and aws_secret_access_key must both be set "
30+ " or both unset" ;
31+ }
32+ if (has_token && !has_key) {
33+ return " aws_session_token requires aws_access_key_id and "
34+ " aws_secret_access_key" ;
35+ }
36+ return std::nullopt ;
1537}
1638
1739Aws::Client::ClientConfiguration BuildConfig (
@@ -39,14 +61,17 @@ Aws::Client::ClientConfiguration BuildConfig(
3961
4062} // namespace
4163
42- std::shared_ptr<Aws::DynamoDB::DynamoDBClient> BuildDynamoDBClient (
43- DynamoDBClientOptions const & options) {
64+ tl::expected<std::shared_ptr<Aws::DynamoDB::DynamoDBClient>, std::string>
65+ BuildDynamoDBClient (DynamoDBClientOptions const & options) {
66+ if (auto err = ValidateCredentials (options)) {
67+ return tl::make_unexpected (std::move (*err));
68+ }
69+
4470 auto const config = BuildConfig (options);
4571
46- if (HasExplicitCredentials ( options) ) {
72+ if (options. aws_access_key_id ) {
4773 Aws::Auth::AWSCredentials credentials{
48- options.aws_access_key_id .value_or (" " ),
49- options.aws_secret_access_key .value_or (" " ),
74+ *options.aws_access_key_id , *options.aws_secret_access_key ,
5075 options.aws_session_token .value_or (" " )};
5176 return std::make_shared<Aws::DynamoDB::DynamoDBClient>(credentials,
5277 config);
0 commit comments