Skip to content

Add basic Credentials wrapper + ManagedNative mixin #20

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

Merged
merged 18 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from 12 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
3 changes: 2 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ Metrics/BlockLength:
Exclude:
- 'gems/aws-crt/Rakefile'
- '**/*.rake'
- 'gems/aws-crt/spec/**/*.rb'
- 'gems/**/spec/**/*.rb'

Metrics/MethodLength:
Max: 25
Exclude:
- 'gems/aws-crt/ext/compile.rb'

Expand Down
2 changes: 1 addition & 1 deletion format-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if NOT type $CLANG_FORMAT 2> /dev/null ; then
fi

FAIL=0
SOURCE_FILES=`find aws-crt/native/src -type f \( -name '*.h' -o -name '*.c' \)`
SOURCE_FILES=`find gems/aws-crt/native/src -type f \( -name '*.h' -o -name '*.c' \)`
for i in $SOURCE_FILES
do
$CLANG_FORMAT -output-replacements-xml $i | grep -c "<replacement " > /dev/null
Expand Down
2 changes: 2 additions & 0 deletions gems/aws-crt-auth/lib/aws-crt-auth.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

require 'aws-crt'
require_relative 'aws-crt-auth/credentials'
require_relative 'aws-crt-auth/signer'

module Aws
module Crt
Expand Down
81 changes: 81 additions & 0 deletions gems/aws-crt-auth/lib/aws-crt-auth/credentials.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

module Aws
module Crt
module Auth
# Utility class for Credentials.
# @api private
class Credentials
include Aws::Crt::ManagedNative
native_destroy Aws::Crt::Native.method(:credentials_release)

UINT64_MAX = 18_446_744_073_709_551_615

# @param [String] access_key_id
# @param [String] secret_access_key
# @param [String] session_token (nil)
# @param [Time|int] expiration (nil) - Either a Time or if an int
# seconds since unix epoch
def initialize(access_key_id, secret_access_key,
session_token = nil, expiration = nil)
if !access_key_id || access_key_id.empty?
raise ArgumentError, 'access_key_id must be set'
end

if !secret_access_key || secret_access_key.empty?
raise ArgumentError, 'secret_access_key must be set'
end

manage_native do
Aws::Crt::Native.credentials_new(
access_key_id,
secret_access_key,
session_token,
expiration&.to_i || UINT64_MAX
)
end
end

# @return [String, nil]
def access_key_id
Aws::Crt::Native.credentials_get_access_key_id(native)
end

# @return [String, nil]
def secret_access_key
Aws::Crt::Native.credentials_get_secret_access_key(native)
end

# @return [String, nil]
def session_token
Aws::Crt::Native.credentials_get_session_token(native)
end

# @return [Time,nil]
def expiration
exp = Aws::Crt::Native.credentials_get_expiration(native)
return if exp == UINT64_MAX

Time.at(exp)
end

# @return [Credentials]
def credentials
self
end

# @return [Boolean] Returns `true` if the access key id and secret
# access key are both set.
def set?
native_set?
end

# Removing the secret access key from the default inspect string.
# @api private
def inspect
"#<#{self.class.name} access_key_id=#{access_key_id.inspect}>"
end
end
end
end
end
37 changes: 37 additions & 0 deletions gems/aws-crt-auth/lib/aws-crt-auth/signer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

module Aws
module Crt
module Auth
# Utility class for creating AWS signature version 4 signature.
class Signer
def initialize(options = {})
@service = extract_service(options)
@region = extract_region(options)
@credentials_provider = extract_credentials_provider(options)
@unsigned_headers = Set.new((options.fetch(:unsigned_headers, []))
.map(&:downcase))
@unsigned_headers << 'authorization'
@unsigned_headers << 'x-amzn-trace-id'
@unsigned_headers << 'expect'
%i[uri_escape_path apply_checksum_header].each do |opt|
instance_variable_set("@#{opt}",
options.key?(opt) ? options[:opt] : true)
end
end

def sign_request(request)
# TODO
end

def sign_event(prior_signature, payload, encoder)
# TODO
end

def presign_url(options)
# TODO
end
end
end
end
end
110 changes: 110 additions & 0 deletions gems/aws-crt-auth/spec/credentials_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# frozen_string_literal: true

require_relative 'spec_helper'
require 'weakref'

module Aws
module Crt
module Auth #:nodoc:
describe Credentials do
describe '#initilize' do
it 'raises an ArgumentError when missing access_key_id' do
expect { Credentials.new(nil, 'secret') }
.to raise_error(ArgumentError)
end

it 'raises an ArgumentError when missing secret_access_key' do
expect { Credentials.new('akid', nil) }
.to raise_error(ArgumentError)
end

it 'defaults the session token to nil' do
expect(Credentials.new('akid', 'secret').session_token).to be nil
end

it 'defaults the expiration to nil' do
expect(Credentials.new('akid', 'secret').expiration)
.to be_nil
end

it 'accepts a Time for expiration' do
exp = Time.now
creds = Credentials.new('akid', 'secret', 'token', exp)
expect(creds.expiration.to_i).to eq exp.to_i
end

it 'accepts an epoch (integer) for expiration' do
exp = Time.now
creds = Credentials.new('akid', 'secret', 'token', exp.to_i)
expect(creds.expiration.to_i).to eq exp.to_i
end
end

describe 'accessors' do
let(:exp) { Time.now }
let(:creds) { Credentials.new('akid', 'secret', 'token', exp) }

it 'provides access to the access key id' do
expect(creds.access_key_id).to eq('akid')
end

it 'provides access to the secret access key' do
expect(creds.secret_access_key).to eq('secret')
end

it 'provides access to the session token' do
expect(creds.session_token).to eq('token')
end

it 'provides access to the expiration' do
expect(creds.expiration.to_i).to eq exp.to_i
end
end

describe '#set?' do
it 'returns true when the key and secret are both non nil values' do
expect(Credentials.new('akid', 'secret').set?).to be(true)
end

it 'returns false after the credentials have been released' do
creds = Credentials.new('akid', 'secret')
creds.release
expect(creds.set?).to be(false)
end
end

describe '#inspect' do
let(:creds) { Credentials.new('akid', 'secret', 'token') }

it 'does not include the secret_access_key' do
expect(creds.inspect).not_to include 'secret'
end
end

describe '.on_release' do
it 'cleans up with release' do
creds = Credentials.new('akid', 'secret')
expect(creds).to_not be_nil

creds.release
check_for_clean_shutdown
end

if garbage_collect_is_immediate?
it 'cleans up with GC' do
creds = Credentials.new('akid', 'secret', 'session')
weakref = WeakRef.new(creds)
expect(weakref.weakref_alive?).to be true

# force cleanup via GC
creds = nil # rubocop:disable Lint/UselessAssignment
ObjectSpace.garbage_collect
expect(weakref.weakref_alive?).to be_falsey
check_for_clean_shutdown
end
end
end
end
end
end
end
9 changes: 9 additions & 0 deletions gems/aws-crt-auth/spec/signer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

require_relative 'spec_helper'

require 'tempfile'
require 'base64'

describe Aws::Crt::Auth::Signer do
end
1 change: 1 addition & 0 deletions gems/aws-crt-auth/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
# use the local version of aws-crt
$LOAD_PATH.unshift File.expand_path('../../aws-crt/lib', __dir__)

require_relative '../../aws-crt/spec/spec_helper'
require 'aws-crt-auth'
1 change: 1 addition & 0 deletions gems/aws-crt/lib/aws-crt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require_relative 'aws-crt/platforms'
require_relative 'aws-crt/native'
require_relative 'aws-crt/errors'
require_relative 'aws-crt/managed_native'
require_relative 'aws-crt/io'

# Top level Amazon Web Services (AWS) namespace
Expand Down
22 changes: 4 additions & 18 deletions gems/aws-crt/lib/aws-crt/io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ module IO
# Classes that need to do async work will ask the EventLoopGroup
# for an event-loop to use.
class EventLoopGroup
include Aws::Crt::ManagedNative
native_destroy Aws::Crt::Native.method(:event_loop_group_release)

def initialize(max_threads = nil)
unless max_threads.nil? ||
(max_threads.is_a?(Integer) && max_threads.positive?)
Expand All @@ -18,26 +21,9 @@ def initialize(max_threads = nil)
# Ruby uses nil to request default values, native code uses 0
max_threads = 0 if max_threads.nil?

native = Aws::Crt.call do
manage_native do
Aws::Crt::Native.event_loop_group_new(max_threads)
end

@native = FFI::AutoPointer.new(native, self.class.method(:on_release))
end

# Immediately release this instance's attachment to the underlying
# resources, without waiting for the garbage collector.
# Note that underlying resources will remain alive until nothing
# else is using them.
def release
return unless @native

@native.free
@native = nil
end

def self.on_release(native)
Aws::Crt::Native.event_loop_group_release(native)
end
end
end
Expand Down
Loading