Skip to content

Commit

Permalink
Merge customizations for DSQL
Browse files Browse the repository at this point in the history
  • Loading branch information
aws-sdk-ruby-automation committed Dec 3, 2024
1 parent cf12113 commit aec9457
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 53 deletions.
4 changes: 4 additions & 0 deletions gems/aws-sdk-dsql/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Unreleased Changes
------------------

* Feature - Add an `AuthTokenGenerator` to generate auth tokens for `DbConnect` and `DbConnectAdmin` actions.
22 changes: 22 additions & 0 deletions gems/aws-sdk-dsql/lib/aws-sdk-dsql.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

# WARNING ABOUT GENERATED CODE
#
# This file is generated. See the contributing guide for more information:
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
#
# WARNING ABOUT GENERATED CODE


require 'aws-sdk-core'
require 'aws-sigv4'

Aws::Plugins::GlobalConfiguration.add_identifier(:dsql)

module Aws::DSQL
# this should get replaced by build

GEM_VERSION = '1.0.0'
end

require_relative 'aws-sdk-dsql/customizations'
3 changes: 3 additions & 0 deletions gems/aws-sdk-dsql/lib/aws-sdk-dsql/customizations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require 'aws-sdk-dsql/customizations/auth_token_generator'
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

require 'aws-sigv4'

module Aws
module DSQL
# A utility class that generates an auth token that supports database
# logins for DSQL clusters. IAM credentials are used for authentication
# instead of the database password.
class AuthTokenGenerator
# @option options [Credentials] :credentials An object that
# responds to `#credentials` returning another object that responds to
# `#access_key_id`, `#secret_access_key`, and `#session_token`.
def initialize(options = {})
@credentials = options.fetch(:credentials)
end

# Generates an auth token for the DbConnect action.
#
# @param [Hash] options
# @option options [String] :region The AWS region where the DSQL Cluster
# is hosted. Defaults to the region of the client.
# @option options [String] :endpoint The DSQL endpoint host name.
# @option options [Integer] :expires_in (900) The number of seconds the
# presigned URL is valid for.
# @return [String]
def generate_db_connect_auth_token(options = {})
presigned_token(options, 'DbConnect')
end

# Generates an auth token for the DbConnectAdmin action.
#
# @param [Hash] options
# @option options [String] :region The AWS region where the DSQL Cluster
# is hosted. Defaults to the region of the client.
# @option options [String] :endpoint The DSQL endpoint host name.
# @option options [Integer] :expires_in (900) The number of seconds the
# token is valid for.
# @return [String]
def generate_db_connect_admin_auth_token(options = {})
presigned_token(options, 'DbConnectAdmin')
end

private

def presigned_token(options, action)
region = options.fetch(:region)
endpoint = options.fetch(:endpoint)

param_list = Aws::Query::ParamList.new
param_list.set('Action', action)

signer = Aws::Sigv4::Signer.new(
service: 'dsql',
region: region,
credentials_provider: @credentials
)

presigned_url = signer.presign_url(
http_method: 'GET',
url: "https://#{endpoint}/?#{param_list}",
body: '',
expires_in: options[:expires_in]
).to_s
# Remove extra scheme for token
presigned_url[8..-1]
end
end
end
end
81 changes: 81 additions & 0 deletions gems/aws-sdk-dsql/spec/auth_token_generator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

require_relative 'spec_helper'

module Aws
module DSQL
describe AuthTokenGenerator do
let(:generator) do
AuthTokenGenerator.new(
credentials: Credentials.new('akid', 'skid')
)
end

describe 'initialize' do
it 'requires :credentials' do
expect { AuthTokenGenerator.new }.to raise_error(KeyError)
end
end

describe 'generate_db_connect_auth_token' do
it 'requires region and endpoint' do
expect do
generator.generate_db_connect_auth_token(region: 'us-west-2')
end.to raise_error(KeyError)
expect do
generator.generate_db_connect_auth_token(
endpoint: 'peccy.dsql.us-east-1.on.aws'
)
end.to raise_error(KeyError)
end

it 'generates a valid token' do
now = Time.parse('20240827T000000Z')
allow(Time).to receive(:now).and_return(now)

region = 'us-east-1'
endpoint = 'peccy.dsql.us-east-1.on.aws'
token = generator.generate_db_connect_auth_token(
region: region,
endpoint: endpoint,
expires_in: 450
)
expect(token).to match(/#{endpoint}\/\?Action=DbConnect/)
expect(token).to match(/X-Amz-Credential=akid%2F#{now.utc.strftime('%Y%m%d')}%2F#{region}%2Fdsql%2Faws4_request/)
expect(token).to match(/X-Amz-Expires=450/)
expect(token).not_to match(/http[s?]:\/\//)
end
end

describe 'db_connect_admin_auth_token' do
it 'requires region and endpoint' do
expect do
generator.generate_db_connect_admin_auth_token(region: 'us-west-2')
end.to raise_error(KeyError)
expect do
generator.generate_db_connect_admin_auth_token(
endpoint: 'peccy.dsql.us-east-1.on.aws'
)
end.to raise_error(KeyError)
end

it 'generates a valid token' do
now = Time.parse('20240827T000000Z')
allow(Time).to receive(:now).and_return(now)

region = 'us-east-1'
endpoint = 'peccy.dsql.us-east-1.on.aws'
token = generator.generate_db_connect_admin_auth_token(
region: region,
endpoint: endpoint,
expires_in: 450
)
expect(token).to match(/#{endpoint}\/\?Action=DbConnectAdmin/)
expect(token).to match(/X-Amz-Credential=akid%2F#{now.utc.strftime('%Y%m%d')}%2F#{region}%2Fdsql%2Faws4_request/)
expect(token).to match(/X-Amz-Expires=450/)
expect(token).not_to match(/http[s?]:\/\//)
end
end
end
end
end
18 changes: 18 additions & 0 deletions gems/aws-sdk-dsql/spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

# WARNING ABOUT GENERATED CODE
#
# This file is generated. See the contributing guide for more information:
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
#
# WARNING ABOUT GENERATED CODE

require_relative '../../aws-sdk-core/spec/shared_spec_helper'

$:.unshift(File.expand_path('../../lib', __FILE__))
$:.unshift(File.expand_path('../../../aws-sdk-core/lib', __FILE__))
$:.unshift(File.expand_path('../../../aws-sigv4/lib', __FILE__))

require 'rspec'
require 'webmock/rspec'
require 'aws-sdk-dsql'
2 changes: 2 additions & 0 deletions gems/aws-sdk-rds/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Feature - Support `expires_in` option for the `AuthTokenGenerator`.

1.261.0 (2024-12-02)
------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module RDS
#
# @see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html
class AuthTokenGenerator
# @option options [required, Credentials] :credentials An object that
# @option options [Credentials] :credentials An object that
# responds to `#credentials` returning another object that responds to
# `#access_key_id`, `#secret_access_key`, and `#session_token`.
def initialize(options = {})
Expand All @@ -19,19 +19,20 @@ def initialize(options = {})

# Creates an auth login token.
#
# @param [Hash] params The parameters for auth token creation.
# @option params [required, String] :region Region where the database
# is located.
# @option params [required, String] :endpoint Hostname of the database
# with a port number.
# For example: my-instance.us-west-2.rds.amazonaws.com:3306
# @option params [required, String] :user_name Username to login as.
#
# @param [Hash] options The options for auth token creation.
# @option options [String] :region The region where the database
# is located.
# @option options [String] :endpoint The hostname of the database
# with a port number.
# For example: my-instance.us-west-2.rds.amazonaws.com:3306
# @option options [String] :user_name The username to login as.
# @option options [Integer] :expires_in (900) The number of seconds the
# token is valid for.
# @return [String]
def auth_token(params)
region = params.fetch(:region)
endpoint = params.fetch(:endpoint)
user_name = params.fetch(:user_name)
def generate_auth_token(options)
region = options.fetch(:region)
endpoint = options.fetch(:endpoint)
user_name = options.fetch(:user_name)

param_list = Aws::Query::ParamList.new
param_list.set('Action', 'connect')
Expand All @@ -47,11 +48,12 @@ def auth_token(params)
http_method: 'GET',
url: "https://#{endpoint}/?#{param_list}",
body: '',
expires_in: 900
expires_in: options[:expires_in]
).to_s
# Remove extra scheme for token
presigned_url[8..-1]
end
alias_method :auth_token, :generate_auth_token
end
end
end
69 changes: 30 additions & 39 deletions gems/aws-sdk-rds/spec/auth_token_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,67 @@

module Aws
module RDS

describe AuthTokenGenerator do

let(:generator) {
let(:generator) do
AuthTokenGenerator.new(
credentials: Credentials.new('akid', 'skid')
credentials: Credentials.new('akid', 'secret')
)
}
end

describe 'initialize' do

it 'requires :credentials parameter' do
expect {
AuthTokenGenerator.new
}.to raise_error(
KeyError
)
it 'requires :credentials' do
expect { AuthTokenGenerator.new }.to raise_error(KeyError)
end

end

describe 'auth_token' do

describe 'generate_auth_token' do
it 'requires region, endpoint, username to create a token' do
expect {
generator.auth_token(
expect do
generator.generate_auth_token(
region: 'us-west-2',
user_name: 'user'
)
}.to raise_error(
KeyError
)
expect {
generator.auth_token(
end.to raise_error(KeyError)
expect do
generator.generate_auth_token(
region: 'us-west-2',
endpoint: 'prod-instance.us-east-1.rds.amazonaws.com:3306'
)
}.to raise_error(
KeyError
)
expect {
generator.auth_token(
end.to raise_error(KeyError)
expect do
generator.generate_auth_token(
endpoint: 'prod-instance.us-east-1.rds.amazonaws.com:3306',
user_name: 'user'
)
}.to raise_error(
KeyError
)
end.to raise_error(KeyError)
end

it 'generates a valid token' do
now = Time.now
now = Time.parse('20240827T000000Z')
allow(Time).to receive(:now).and_return(now)

region = 'us-west-2'
region = 'us-east-1'
endpoint = 'prod-instance.us-east-1.rds.amazonaws.com:3306'
user_name = 'mySQLUser'
token = generator.auth_token(
user_name = 'peccy'
token = generator.generate_auth_token(
region: region,
endpoint: endpoint,
user_name: user_name
user_name: user_name,
expires_in: 450
)
expect(token).to match(/#{endpoint}\/\?Action=connect/)
expect(token).to match(/DBUser=#{user_name}/)
expect(token).to match(/#{endpoint}\/\?Action=connect&DBUser=#{user_name}/)
expect(token).to match(/X-Amz-Credential=akid%2F#{now.utc.strftime('%Y%m%d')}%2F#{region}%2Frds-db%2Faws4_request/)
expect(token).to match(/X-Amz-Expires=450/)
expect(token).not_to match(/http[s?]:\/\//)
end

end

describe 'auth_token' do
it 'is an alias for generate_auth_token' do
expect(generator.method(:auth_token))
.to eq(generator.method(:generate_auth_token))
end
end
end

end
end

0 comments on commit aec9457

Please sign in to comment.