Skip to content

Commit aec9457

Browse files
Merge customizations for DSQL
1 parent cf12113 commit aec9457

File tree

9 files changed

+246
-53
lines changed

9 files changed

+246
-53
lines changed

gems/aws-sdk-dsql/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Unreleased Changes
2+
------------------
3+
4+
* Feature - Add an `AuthTokenGenerator` to generate auth tokens for `DbConnect` and `DbConnectAdmin` actions.

gems/aws-sdk-dsql/lib/aws-sdk-dsql.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
# WARNING ABOUT GENERATED CODE
4+
#
5+
# This file is generated. See the contributing guide for more information:
6+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
7+
#
8+
# WARNING ABOUT GENERATED CODE
9+
10+
11+
require 'aws-sdk-core'
12+
require 'aws-sigv4'
13+
14+
Aws::Plugins::GlobalConfiguration.add_identifier(:dsql)
15+
16+
module Aws::DSQL
17+
# this should get replaced by build
18+
19+
GEM_VERSION = '1.0.0'
20+
end
21+
22+
require_relative 'aws-sdk-dsql/customizations'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# frozen_string_literal: true
2+
3+
require 'aws-sdk-dsql/customizations/auth_token_generator'
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# frozen_string_literal: true
2+
3+
require 'aws-sigv4'
4+
5+
module Aws
6+
module DSQL
7+
# A utility class that generates an auth token that supports database
8+
# logins for DSQL clusters. IAM credentials are used for authentication
9+
# instead of the database password.
10+
class AuthTokenGenerator
11+
# @option options [Credentials] :credentials An object that
12+
# responds to `#credentials` returning another object that responds to
13+
# `#access_key_id`, `#secret_access_key`, and `#session_token`.
14+
def initialize(options = {})
15+
@credentials = options.fetch(:credentials)
16+
end
17+
18+
# Generates an auth token for the DbConnect action.
19+
#
20+
# @param [Hash] options
21+
# @option options [String] :region The AWS region where the DSQL Cluster
22+
# is hosted. Defaults to the region of the client.
23+
# @option options [String] :endpoint The DSQL endpoint host name.
24+
# @option options [Integer] :expires_in (900) The number of seconds the
25+
# presigned URL is valid for.
26+
# @return [String]
27+
def generate_db_connect_auth_token(options = {})
28+
presigned_token(options, 'DbConnect')
29+
end
30+
31+
# Generates an auth token for the DbConnectAdmin action.
32+
#
33+
# @param [Hash] options
34+
# @option options [String] :region The AWS region where the DSQL Cluster
35+
# is hosted. Defaults to the region of the client.
36+
# @option options [String] :endpoint The DSQL endpoint host name.
37+
# @option options [Integer] :expires_in (900) The number of seconds the
38+
# token is valid for.
39+
# @return [String]
40+
def generate_db_connect_admin_auth_token(options = {})
41+
presigned_token(options, 'DbConnectAdmin')
42+
end
43+
44+
private
45+
46+
def presigned_token(options, action)
47+
region = options.fetch(:region)
48+
endpoint = options.fetch(:endpoint)
49+
50+
param_list = Aws::Query::ParamList.new
51+
param_list.set('Action', action)
52+
53+
signer = Aws::Sigv4::Signer.new(
54+
service: 'dsql',
55+
region: region,
56+
credentials_provider: @credentials
57+
)
58+
59+
presigned_url = signer.presign_url(
60+
http_method: 'GET',
61+
url: "https://#{endpoint}/?#{param_list}",
62+
body: '',
63+
expires_in: options[:expires_in]
64+
).to_s
65+
# Remove extra scheme for token
66+
presigned_url[8..-1]
67+
end
68+
end
69+
end
70+
end
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'spec_helper'
4+
5+
module Aws
6+
module DSQL
7+
describe AuthTokenGenerator do
8+
let(:generator) do
9+
AuthTokenGenerator.new(
10+
credentials: Credentials.new('akid', 'skid')
11+
)
12+
end
13+
14+
describe 'initialize' do
15+
it 'requires :credentials' do
16+
expect { AuthTokenGenerator.new }.to raise_error(KeyError)
17+
end
18+
end
19+
20+
describe 'generate_db_connect_auth_token' do
21+
it 'requires region and endpoint' do
22+
expect do
23+
generator.generate_db_connect_auth_token(region: 'us-west-2')
24+
end.to raise_error(KeyError)
25+
expect do
26+
generator.generate_db_connect_auth_token(
27+
endpoint: 'peccy.dsql.us-east-1.on.aws'
28+
)
29+
end.to raise_error(KeyError)
30+
end
31+
32+
it 'generates a valid token' do
33+
now = Time.parse('20240827T000000Z')
34+
allow(Time).to receive(:now).and_return(now)
35+
36+
region = 'us-east-1'
37+
endpoint = 'peccy.dsql.us-east-1.on.aws'
38+
token = generator.generate_db_connect_auth_token(
39+
region: region,
40+
endpoint: endpoint,
41+
expires_in: 450
42+
)
43+
expect(token).to match(/#{endpoint}\/\?Action=DbConnect/)
44+
expect(token).to match(/X-Amz-Credential=akid%2F#{now.utc.strftime('%Y%m%d')}%2F#{region}%2Fdsql%2Faws4_request/)
45+
expect(token).to match(/X-Amz-Expires=450/)
46+
expect(token).not_to match(/http[s?]:\/\//)
47+
end
48+
end
49+
50+
describe 'db_connect_admin_auth_token' do
51+
it 'requires region and endpoint' do
52+
expect do
53+
generator.generate_db_connect_admin_auth_token(region: 'us-west-2')
54+
end.to raise_error(KeyError)
55+
expect do
56+
generator.generate_db_connect_admin_auth_token(
57+
endpoint: 'peccy.dsql.us-east-1.on.aws'
58+
)
59+
end.to raise_error(KeyError)
60+
end
61+
62+
it 'generates a valid token' do
63+
now = Time.parse('20240827T000000Z')
64+
allow(Time).to receive(:now).and_return(now)
65+
66+
region = 'us-east-1'
67+
endpoint = 'peccy.dsql.us-east-1.on.aws'
68+
token = generator.generate_db_connect_admin_auth_token(
69+
region: region,
70+
endpoint: endpoint,
71+
expires_in: 450
72+
)
73+
expect(token).to match(/#{endpoint}\/\?Action=DbConnectAdmin/)
74+
expect(token).to match(/X-Amz-Credential=akid%2F#{now.utc.strftime('%Y%m%d')}%2F#{region}%2Fdsql%2Faws4_request/)
75+
expect(token).to match(/X-Amz-Expires=450/)
76+
expect(token).not_to match(/http[s?]:\/\//)
77+
end
78+
end
79+
end
80+
end
81+
end

gems/aws-sdk-dsql/spec/spec_helper.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
# WARNING ABOUT GENERATED CODE
4+
#
5+
# This file is generated. See the contributing guide for more information:
6+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
7+
#
8+
# WARNING ABOUT GENERATED CODE
9+
10+
require_relative '../../aws-sdk-core/spec/shared_spec_helper'
11+
12+
$:.unshift(File.expand_path('../../lib', __FILE__))
13+
$:.unshift(File.expand_path('../../../aws-sdk-core/lib', __FILE__))
14+
$:.unshift(File.expand_path('../../../aws-sigv4/lib', __FILE__))
15+
16+
require 'rspec'
17+
require 'webmock/rspec'
18+
require 'aws-sdk-dsql'

gems/aws-sdk-rds/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Unreleased Changes
22
------------------
33

4+
* Feature - Support `expires_in` option for the `AuthTokenGenerator`.
5+
46
1.261.0 (2024-12-02)
57
------------------
68

gems/aws-sdk-rds/lib/aws-sdk-rds/customizations/auth_token_generator.rb

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module RDS
1010
#
1111
# @see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html
1212
class AuthTokenGenerator
13-
# @option options [required, Credentials] :credentials An object that
13+
# @option options [Credentials] :credentials An object that
1414
# responds to `#credentials` returning another object that responds to
1515
# `#access_key_id`, `#secret_access_key`, and `#session_token`.
1616
def initialize(options = {})
@@ -19,19 +19,20 @@ def initialize(options = {})
1919

2020
# Creates an auth login token.
2121
#
22-
# @param [Hash] params The parameters for auth token creation.
23-
# @option params [required, String] :region Region where the database
24-
# is located.
25-
# @option params [required, String] :endpoint Hostname of the database
26-
# with a port number.
27-
# For example: my-instance.us-west-2.rds.amazonaws.com:3306
28-
# @option params [required, String] :user_name Username to login as.
29-
#
22+
# @param [Hash] options The options for auth token creation.
23+
# @option options [String] :region The region where the database
24+
# is located.
25+
# @option options [String] :endpoint The hostname of the database
26+
# with a port number.
27+
# For example: my-instance.us-west-2.rds.amazonaws.com:3306
28+
# @option options [String] :user_name The username to login as.
29+
# @option options [Integer] :expires_in (900) The number of seconds the
30+
# token is valid for.
3031
# @return [String]
31-
def auth_token(params)
32-
region = params.fetch(:region)
33-
endpoint = params.fetch(:endpoint)
34-
user_name = params.fetch(:user_name)
32+
def generate_auth_token(options)
33+
region = options.fetch(:region)
34+
endpoint = options.fetch(:endpoint)
35+
user_name = options.fetch(:user_name)
3536

3637
param_list = Aws::Query::ParamList.new
3738
param_list.set('Action', 'connect')
@@ -47,11 +48,12 @@ def auth_token(params)
4748
http_method: 'GET',
4849
url: "https://#{endpoint}/?#{param_list}",
4950
body: '',
50-
expires_in: 900
51+
expires_in: options[:expires_in]
5152
).to_s
5253
# Remove extra scheme for token
5354
presigned_url[8..-1]
5455
end
56+
alias_method :auth_token, :generate_auth_token
5557
end
5658
end
5759
end

gems/aws-sdk-rds/spec/auth_token_generator_spec.rb

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,76 +4,67 @@
44

55
module Aws
66
module RDS
7-
87
describe AuthTokenGenerator do
9-
10-
let(:generator) {
8+
let(:generator) do
119
AuthTokenGenerator.new(
12-
credentials: Credentials.new('akid', 'skid')
10+
credentials: Credentials.new('akid', 'secret')
1311
)
14-
}
12+
end
1513

1614
describe 'initialize' do
17-
18-
it 'requires :credentials parameter' do
19-
expect {
20-
AuthTokenGenerator.new
21-
}.to raise_error(
22-
KeyError
23-
)
15+
it 'requires :credentials' do
16+
expect { AuthTokenGenerator.new }.to raise_error(KeyError)
2417
end
25-
2618
end
2719

28-
describe 'auth_token' do
29-
20+
describe 'generate_auth_token' do
3021
it 'requires region, endpoint, username to create a token' do
31-
expect {
32-
generator.auth_token(
22+
expect do
23+
generator.generate_auth_token(
3324
region: 'us-west-2',
3425
user_name: 'user'
3526
)
36-
}.to raise_error(
37-
KeyError
38-
)
39-
expect {
40-
generator.auth_token(
27+
end.to raise_error(KeyError)
28+
expect do
29+
generator.generate_auth_token(
4130
region: 'us-west-2',
4231
endpoint: 'prod-instance.us-east-1.rds.amazonaws.com:3306'
4332
)
44-
}.to raise_error(
45-
KeyError
46-
)
47-
expect {
48-
generator.auth_token(
33+
end.to raise_error(KeyError)
34+
expect do
35+
generator.generate_auth_token(
4936
endpoint: 'prod-instance.us-east-1.rds.amazonaws.com:3306',
5037
user_name: 'user'
5138
)
52-
}.to raise_error(
53-
KeyError
54-
)
39+
end.to raise_error(KeyError)
5540
end
5641

5742
it 'generates a valid token' do
58-
now = Time.now
43+
now = Time.parse('20240827T000000Z')
5944
allow(Time).to receive(:now).and_return(now)
6045

61-
region = 'us-west-2'
46+
region = 'us-east-1'
6247
endpoint = 'prod-instance.us-east-1.rds.amazonaws.com:3306'
63-
user_name = 'mySQLUser'
64-
token = generator.auth_token(
48+
user_name = 'peccy'
49+
token = generator.generate_auth_token(
6550
region: region,
6651
endpoint: endpoint,
67-
user_name: user_name
52+
user_name: user_name,
53+
expires_in: 450
6854
)
69-
expect(token).to match(/#{endpoint}\/\?Action=connect/)
70-
expect(token).to match(/DBUser=#{user_name}/)
55+
expect(token).to match(/#{endpoint}\/\?Action=connect&DBUser=#{user_name}/)
7156
expect(token).to match(/X-Amz-Credential=akid%2F#{now.utc.strftime('%Y%m%d')}%2F#{region}%2Frds-db%2Faws4_request/)
57+
expect(token).to match(/X-Amz-Expires=450/)
58+
expect(token).not_to match(/http[s?]:\/\//)
7259
end
73-
7460
end
7561

62+
describe 'auth_token' do
63+
it 'is an alias for generate_auth_token' do
64+
expect(generator.method(:auth_token))
65+
.to eq(generator.method(:generate_auth_token))
66+
end
67+
end
7668
end
77-
7869
end
7970
end

0 commit comments

Comments
 (0)