aws_assume_role 1.1.0-universal-freebsd

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rubocop.yml +57 -0
  4. data/.ruby-version +1 -0
  5. data/.simplecov +22 -0
  6. data/.travis.yml +24 -0
  7. data/CHANGELOG.md +61 -0
  8. data/Gemfile +18 -0
  9. data/LICENSE.md +201 -0
  10. data/README.md +303 -0
  11. data/Rakefile +63 -0
  12. data/aws_assume_role.gemspec +56 -0
  13. data/bin/aws-assume-role +4 -0
  14. data/i18n/en.yml +109 -0
  15. data/lib/aws_assume_role/cli/actions/abstract_action.rb +61 -0
  16. data/lib/aws_assume_role/cli/actions/configure_profile.rb +24 -0
  17. data/lib/aws_assume_role/cli/actions/configure_role_assumption.rb +22 -0
  18. data/lib/aws_assume_role/cli/actions/console.rb +70 -0
  19. data/lib/aws_assume_role/cli/actions/delete_profile.rb +22 -0
  20. data/lib/aws_assume_role/cli/actions/includes.rb +12 -0
  21. data/lib/aws_assume_role/cli/actions/list_profiles.rb +12 -0
  22. data/lib/aws_assume_role/cli/actions/migrate_profile.rb +20 -0
  23. data/lib/aws_assume_role/cli/actions/reset_environment.rb +50 -0
  24. data/lib/aws_assume_role/cli/actions/run.rb +36 -0
  25. data/lib/aws_assume_role/cli/actions/set_environment.rb +62 -0
  26. data/lib/aws_assume_role/cli/actions/test.rb +35 -0
  27. data/lib/aws_assume_role/cli/commands/configure.rb +32 -0
  28. data/lib/aws_assume_role/cli/commands/console.rb +19 -0
  29. data/lib/aws_assume_role/cli/commands/delete.rb +13 -0
  30. data/lib/aws_assume_role/cli/commands/environment.rb +34 -0
  31. data/lib/aws_assume_role/cli/commands/list.rb +12 -0
  32. data/lib/aws_assume_role/cli/commands/migrate.rb +13 -0
  33. data/lib/aws_assume_role/cli/commands/run.rb +19 -0
  34. data/lib/aws_assume_role/cli/commands/test.rb +20 -0
  35. data/lib/aws_assume_role/cli/includes.rb +3 -0
  36. data/lib/aws_assume_role/cli.rb +20 -0
  37. data/lib/aws_assume_role/configuration.rb +30 -0
  38. data/lib/aws_assume_role/core_ext/aws-sdk/credential_provider_chain.rb +4 -0
  39. data/lib/aws_assume_role/core_ext/aws-sdk/includes.rb +9 -0
  40. data/lib/aws_assume_role/credentials/factories/abstract_factory.rb +33 -0
  41. data/lib/aws_assume_role/credentials/factories/assume_role.rb +39 -0
  42. data/lib/aws_assume_role/credentials/factories/default_chain_provider.rb +113 -0
  43. data/lib/aws_assume_role/credentials/factories/environment.rb +26 -0
  44. data/lib/aws_assume_role/credentials/factories/includes.rb +15 -0
  45. data/lib/aws_assume_role/credentials/factories/instance_profile.rb +19 -0
  46. data/lib/aws_assume_role/credentials/factories/repository.rb +37 -0
  47. data/lib/aws_assume_role/credentials/factories/shared.rb +19 -0
  48. data/lib/aws_assume_role/credentials/factories/static.rb +18 -0
  49. data/lib/aws_assume_role/credentials/factories.rb +11 -0
  50. data/lib/aws_assume_role/credentials/includes.rb +6 -0
  51. data/lib/aws_assume_role/credentials/providers/assume_role_credentials.rb +60 -0
  52. data/lib/aws_assume_role/credentials/providers/includes.rb +9 -0
  53. data/lib/aws_assume_role/credentials/providers/mfa_session_credentials.rb +119 -0
  54. data/lib/aws_assume_role/credentials/providers/shared_keyring_credentials.rb +41 -0
  55. data/lib/aws_assume_role/includes.rb +38 -0
  56. data/lib/aws_assume_role/logging.rb +27 -0
  57. data/lib/aws_assume_role/profile_configuration.rb +73 -0
  58. data/lib/aws_assume_role/runner.rb +40 -0
  59. data/lib/aws_assume_role/store/includes.rb +8 -0
  60. data/lib/aws_assume_role/store/keyring.rb +61 -0
  61. data/lib/aws_assume_role/store/serialization.rb +20 -0
  62. data/lib/aws_assume_role/store/shared_config_with_keyring.rb +250 -0
  63. data/lib/aws_assume_role/types.rb +31 -0
  64. data/lib/aws_assume_role/ui.rb +57 -0
  65. data/lib/aws_assume_role/vendored/aws/README.md +2 -0
  66. data/lib/aws_assume_role/vendored/aws/assume_role_credentials.rb +67 -0
  67. data/lib/aws_assume_role/vendored/aws/includes.rb +9 -0
  68. data/lib/aws_assume_role/vendored/aws/refreshing_credentials.rb +58 -0
  69. data/lib/aws_assume_role/vendored/aws/shared_config.rb +223 -0
  70. data/lib/aws_assume_role/vendored/aws.rb +4 -0
  71. data/lib/aws_assume_role/version.rb +5 -0
  72. data/lib/aws_assume_role.rb +4 -0
  73. metadata +438 -0
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "includes"
4
+ require_relative "../../logging"
5
+ require_relative "../../profile_configuration"
6
+ require_relative "abstract_factory"
7
+ require_relative "environment"
8
+ require_relative "repository"
9
+ require_relative "instance_profile"
10
+ require_relative "assume_role"
11
+ require_relative "shared"
12
+ require_relative "static"
13
+
14
+ class AwsAssumeRole::Credentials::Factories::DefaultChainProvider < Dry::Struct
15
+ constructor_type :schema
16
+ include AwsAssumeRole::Credentials::Factories
17
+ include AwsAssumeRole::Logging
18
+
19
+ attribute :access_key_id, Dry::Types["strict.string"].optional
20
+ attribute :credentials, Dry::Types["object"].optional
21
+ attribute :duration_seconds, Dry::Types["coercible.int"].optional
22
+ attribute :external_id, Dry::Types["strict.string"].optional
23
+ attribute :instance_profile_credentials_retries, Dry::Types["strict.int"].default(0)
24
+ attribute :instance_profile_credentials_timeout, Dry::Types["coercible.float"].default(1.0)
25
+ attribute :mfa_serial, Dry::Types["strict.string"].optional
26
+ attribute :no_profile, Dry::Types["strict.bool"].default(false)
27
+ attribute :path, Dry::Types["strict.string"].optional
28
+ attribute :persist_session, Dry::Types["strict.bool"].default(true)
29
+ attribute :profile_name, Dry::Types["strict.string"].optional
30
+ attribute :profile, Dry::Types["strict.string"].optional
31
+ attribute :region, Dry::Types["strict.string"].optional
32
+ attribute :role_arn, Dry::Types["strict.string"].optional
33
+ attribute :role_session_name, Dry::Types["strict.string"].optional
34
+ attribute :secret_access_key, Dry::Types["strict.string"].optional
35
+ attribute :serial_number, Dry::Types["strict.string"].optional
36
+ attribute :session_token, Dry::Types["strict.string"].optional
37
+ attribute :source_profile, Dry::Types["strict.string"].optional
38
+ attribute :use_mfa, Dry::Types["strict.bool"].default(false)
39
+ attribute :yubikey_oath_name, Dry::Types["strict.string"].optional
40
+
41
+ def self.new(options)
42
+ if options.respond_to? :resolve
43
+ finalize_instance new_with_seahorse(options)
44
+ else
45
+ finalize_instance(options)
46
+ end
47
+ end
48
+
49
+ def self.finalize_instance(options)
50
+ new_opts = options.to_h
51
+ new_opts[:profile_name] ||= new_opts[:profile]
52
+ new_opts[:original_profile] = new_opts[:profile_name]
53
+ instance = allocate
54
+ instance.send(:initialize, new_opts)
55
+ instance
56
+ end
57
+
58
+ def self.new_with_seahorse(resolver)
59
+ keys = resolver.resolve
60
+ options = keys.map do |k|
61
+ [k, resolver.send(k)]
62
+ end
63
+ finalize_instance(options.to_h)
64
+ end
65
+
66
+ def resolve(nil_with_role_not_set: false, explicit_default_profile: false)
67
+ resolve_final_credentials(explicit_default_profile)
68
+ # nil_creds = Aws::Credentials.new(nil, nil, nil)
69
+ return nil if (nil_with_role_not_set &&
70
+ @role_arn &&
71
+ @credentials.credentials.session_token.nil?) || @credentials.nil?
72
+ @credentials
73
+ end
74
+
75
+ def to_h
76
+ to_hash
77
+ end
78
+
79
+ private
80
+
81
+ def resolve_final_credentials(explicit_default_profile = false)
82
+ resolve_credentials(:credential_provider, true, explicit_default_profile)
83
+ return @credentials if @credentials && @credentials.set? && !use_mfa && !role_arn
84
+ resolve_credentials(:second_factor_provider, true, explicit_default_profile)
85
+ return @credentials if @credentials && @credentials.set?
86
+ resolve_credentials(:instance_role_provider, true, explicit_default_profile)
87
+ return @credentials if @credentials && @credentials.set?
88
+ nil
89
+ end
90
+
91
+ def resolve_credentials(type, break_if_successful = false, explicit_default_profile = false)
92
+ factories_to_try = Repository.factories[type]
93
+ factories_to_try.each do |x|
94
+ options = to_h
95
+ options[:credentials] = credentials if credentials && credentials.set?
96
+ logger.debug "About to try credential lookup with #{x}"
97
+ factory = x.new(options)
98
+ @region ||= factory.region
99
+ @profile ||= factory.profile
100
+ @role_arn ||= factory.role_arn
101
+ next unless factory.credentials && factory.credentials.set?
102
+ logger.debug "Profile currently #{@profile}"
103
+ next if explicit_default_profile && (@profile == "default") && (@profile != @original_profile)
104
+ @credentials ||= factory.credentials
105
+ logger.debug "Got #{@credentials}"
106
+ break if break_if_successful
107
+ end
108
+ end
109
+ end
110
+
111
+ module AwsAssumeRole
112
+ DefaultProvider = AwsAssumeRole::Credentials::Factories::DefaultChainProvider
113
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract_factory"
4
+
5
+ class AwsAssumeRole::Credentials::Factories::Environment < AwsAssumeRole::Credentials::Factories::AbstractFactory
6
+ type :credential_provider
7
+ priority 10
8
+
9
+ def initialize(_options, **)
10
+ key = %w[AWS_ACCESS_KEY_ID AMAZON_ACCESS_KEY_ID AWS_ACCESS_KEY]
11
+ secret = %w[AWS_SECRET_ACCESS_KEY AMAZON_SECRET_ACCESS_KEY AWS_SECRET_KEY]
12
+ token = %w[AWS_SESSION_TOKEN AMAZON_SESSION_TOKEN]
13
+ region = %w[AWS_DEFAULT_REGION]
14
+ profile = %w[AWS_PROFILE]
15
+ @credentials = Aws::Credentials.new(envar(key), envar(secret), envar(token))
16
+ @region = envar(region)
17
+ @profile = envar(profile)
18
+ end
19
+
20
+ def envar(keys)
21
+ keys.each do |key|
22
+ return ENV[key] if ENV.key?(key)
23
+ end
24
+ nil
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../includes"
4
+ require_relative "../../logging"
5
+ require_relative "../../vendored/aws"
6
+ require_relative "../../../aws_assume_role"
7
+
8
+ module AwsAssumeRole::Credentials
9
+ module Factories
10
+ Types = Dry::Types.module
11
+ include AwsAssumeRole
12
+ include AwsAssumeRole::Logging
13
+ include AwsAssumeRole::Vendored::Aws
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract_factory"
4
+
5
+ class AwsAssumeRole::Credentials::Factories::InstanceProfile < AwsAssumeRole::Credentials::Factories::AbstractFactory
6
+ type :instance_role_provider
7
+ priority 40
8
+
9
+ def initialize(options = {})
10
+ options[:retries] ||= options[:instance_profile_credentials_retries] || 0
11
+ options[:http_open_timeout] ||= options[:instance_profile_credentials_timeout] || 1
12
+ options[:http_read_timeout] ||= options[:instance_profile_credentials_timeout] || 1
13
+ @credentials = if ENV["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"]
14
+ Aws::ECSCredentials.new(options)
15
+ else
16
+ Aws::InstanceProfileCredentials.new(options)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "includes"
4
+ require_relative "abstract_factory"
5
+
6
+ class AwsAssumeRole::Credentials::Factories::Repository
7
+ include AwsAssumeRole::Credentials::Factories
8
+
9
+ SubFactoryRepositoryType = Types::Hash.schema(Types::Coercible::Int => Types::Strict::Array)
10
+
11
+ FactoryRepositoryType = Types::Hash.schema(
12
+ credential_provider: SubFactoryRepositoryType,
13
+ second_factor_provider: SubFactoryRepositoryType,
14
+ instance_role_provider: SubFactoryRepositoryType,
15
+ )
16
+
17
+ def self.factories
18
+ repository.keys.map { |t| [t, flatten_factory_type_list(t)] }.to_h
19
+ end
20
+
21
+ def self.repository
22
+ @repository ||= FactoryRepositoryType[
23
+ credential_provider: {},
24
+ second_factor_provider: {},
25
+ instance_role_provider: {},
26
+ ]
27
+ end
28
+
29
+ def self.register_factory(klass, type, priority)
30
+ repository[type][priority] ||= []
31
+ repository[type][priority] << klass
32
+ end
33
+
34
+ def self.flatten_factory_type_list(type)
35
+ repository[type].keys.sort.map { |x| @repository[type][x] }.flatten
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract_factory"
4
+ require_relative "../providers/shared_keyring_credentials"
5
+
6
+ class AwsAssumeRole::Credentials::Factories::Shared < AwsAssumeRole::Credentials::Factories::AbstractFactory
7
+ type :credential_provider
8
+ priority 30
9
+
10
+ def initialize(options = {})
11
+ logger.debug "Shared Factory initiated with #{options}"
12
+ @profile = options[:profile]
13
+ @credentials = AwsAssumeRole::Credentials::Providers::SharedKeyringCredentials.new(options)
14
+ @region = @credentials.region
15
+ @role_arn = @credentials.role_arn
16
+ rescue Aws::Errors::NoSuchProfileError
17
+ nil
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract_factory"
4
+
5
+ class AwsAssumeRole::Credentials::Factories::Static < AwsAssumeRole::Credentials::Factories::AbstractFactory
6
+ type :credential_provider
7
+ priority 0
8
+
9
+ def initialize(options = {})
10
+ @credentials = Aws::Credentials.new(
11
+ options[:access_key_id],
12
+ options[:secret_access_key],
13
+ options[:session_token],
14
+ )
15
+ @region = options[:region]
16
+ @profile = options[:profile]
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "factories/repository"
4
+ require_relative "factories/abstract_factory"
5
+ require_relative "factories/default_chain_provider"
6
+ require_relative "factories/assume_role"
7
+ require_relative "factories/environment"
8
+ require_relative "factories/instance_profile"
9
+ require_relative "factories/shared_keyring"
10
+ require_relative "factories/shared"
11
+ require_relative "factories/static"
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../includes"
4
+ module AwsAssumeRole
5
+ module Credentials end
6
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "includes"
4
+ require "set"
5
+
6
+ class AwsAssumeRole::Credentials::Providers::AssumeRoleCredentials
7
+ include AwsAssumeRole::Vendored::Aws::CredentialProvider
8
+ include AwsAssumeRole::Vendored::Aws::RefreshingCredentials
9
+
10
+ # @option options [required, String] :role_arn
11
+ # @option options [required, String] :role_session_name
12
+ # @option options [String] :policy
13
+ # @option options [Integer] :duration_seconds
14
+ # @option options [String] :external_id
15
+ # @option options [STS::Client] :client
16
+ #
17
+ #
18
+
19
+ STS_KEYS = %i[role_arn role_session_name policy duration_seconds external_id client credentials region].freeze
20
+
21
+ def initialize(options = {})
22
+ client_opts = {}
23
+ @assume_role_params = {}
24
+ options.each_pair do |key, value|
25
+ if self.class.assume_role_options.include?(key)
26
+ @assume_role_params[key] = value
27
+ else
28
+ next unless STS_KEYS.include?(key)
29
+ client_opts[key] = value
30
+ end
31
+ end
32
+ @client = client_opts[:client] || ::Aws::STS::Client.new(client_opts)
33
+ super
34
+ end
35
+
36
+ # @return [STS::Client]
37
+ attr_reader :client
38
+
39
+ private
40
+
41
+ def refresh
42
+ c = @client.assume_role(@assume_role_params).credentials
43
+ @credentials = ::Aws::Credentials.new(
44
+ c.access_key_id,
45
+ c.secret_access_key,
46
+ c.session_token,
47
+ )
48
+ @expiration = c.expiration
49
+ end
50
+
51
+ class << self
52
+ # @api private
53
+ def assume_role_options
54
+ @aro ||= begin
55
+ input = ::Aws::STS::Client.api.operation(:assume_role).input
56
+ Set.new(input.shape.member_names)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../includes"
4
+ require_relative "../../vendored/aws"
5
+ require_relative "../../ui"
6
+ require_relative "../../logging"
7
+ module AwsAssumeRole::Credentials
8
+ module Providers end
9
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "includes"
4
+ require_relative "../../types"
5
+ require_relative "../../configuration"
6
+ begin
7
+ require "smartcard"
8
+ require "yubioath"
9
+ SMARTCARD_SUPPORT = true
10
+ rescue LoadError
11
+ SMARTCARD_SUPPORT = false
12
+ end
13
+
14
+ class AwsAssumeRole::Credentials::Providers::MfaSessionCredentials < Dry::Struct
15
+ constructor_type :schema
16
+ include AwsAssumeRole::Vendored::Aws::CredentialProvider
17
+ include AwsAssumeRole::Vendored::Aws::RefreshingCredentials
18
+ include AwsAssumeRole::Ui
19
+ include AwsAssumeRole::Logging
20
+
21
+ attribute :permanent_credentials, Dry::Types["object"].optional
22
+ attribute :credentials, Dry::Types["object"].optional
23
+ attribute :expiration, Dry::Types["strict.time"].default(Time.now)
24
+ attribute :first_time, Dry::Types["strict.bool"].default(true)
25
+ attribute :persist_session, Dry::Types["strict.bool"].default(true)
26
+ attribute :duration_seconds, Dry::Types["coercible.int"].default(3600)
27
+ attribute :region, AwsAssumeRole::Types::Region.optional
28
+ attribute :serial_number, AwsAssumeRole::Types::MfaSerial.optional.default("automatic")
29
+ attribute :yubikey_oath_name, Dry::Types["strict.string"].optional
30
+
31
+ def initialize(options)
32
+ options.each { |key, value| instance_variable_set("@#{key}", value) }
33
+ @permanent_credentials ||= @credentials
34
+ @credentials = nil
35
+ @serial_number = resolve_serial_number(serial_number)
36
+ AwsAssumeRole::Vendored::Aws::RefreshingCredentials.instance_method(:initialize).bind(self).call(options)
37
+ end
38
+
39
+ private
40
+
41
+ def keyring_username
42
+ @keyring_username ||= "#{@identity.to_json}|#{@serial_number}"
43
+ end
44
+
45
+ def sts_client
46
+ @sts_client ||= Aws::STS::Client.new(region: @region, credentials: @permanent_credentials)
47
+ end
48
+
49
+ def prompt_for_token
50
+ text = @first_time ? t("options.mfa_token.first_time") : t("options.mfa_token.other_times")
51
+ Ui.input.ask text
52
+ end
53
+
54
+ def initialized
55
+ @first_time = false
56
+ end
57
+
58
+ def refresh
59
+ return set_credentials_from_keyring if @persist_session && @first_time
60
+ refresh_using_mfa if near_expiration?
61
+ broadcast(:mfa_completed)
62
+ end
63
+
64
+ def retrieve_yubikey_token
65
+ raise t("options.mfa_token.smartcard_not_supported") unless SMARTCARD_SUPPORT
66
+ context = Smartcard::PCSC::Context.new
67
+ raise "Yubikey not found" unless context.readers.length == 1
68
+ reader_name = context.readers.first
69
+ card = Smartcard::PCSC::Card.new(context, reader_name, :shared)
70
+ codes = YubiOATH.new(card).calculate_all(timestamp: Time.now)
71
+ codes.fetch(BinData::String.new(@yubikey_oath_name))
72
+ end
73
+
74
+ def refresh_using_mfa
75
+ token_code = @yubikey_oath_name ? retrieve_yubikey_token : prompt_for_token
76
+ token = sts_client.get_session_token(
77
+ duration_seconds: @duration_seconds,
78
+ serial_number: @serial_number,
79
+ token_code: token_code,
80
+ )
81
+ initialized
82
+ instance_credentials token.credentials
83
+ persist_credentials if @persist_session
84
+ end
85
+
86
+ def credentials_from_keyring
87
+ @credentials_from_keyring ||= AwsAssumeRole::Store::Keyring.fetch keyring_username
88
+ rescue Aws::Errors::NoSuchProfileError
89
+ logger.debug "Key not found"
90
+ @credentials_from_keyring = nil
91
+ return nil
92
+ end
93
+
94
+ def persist_credentials
95
+ AwsAssumeRole::Store::Keyring.save_credentials keyring_username, @credentials, expiration: @expiration
96
+ end
97
+
98
+ def instance_credentials(credentials)
99
+ return unless credentials
100
+ @credentials = AwsAssumeRole::Store::Serialization.credentials_from_hash(credentials)
101
+ @expiration = credentials.respond_to?(:expiration) ? credentials.expiration : Time.parse(credentials[:expiration])
102
+ end
103
+
104
+ def set_credentials_from_keyring
105
+ instance_credentials credentials_from_keyring if credentials_from_keyring
106
+ initialized
107
+ refresh_using_mfa unless @credentials && !near_expiration?
108
+ end
109
+
110
+ def identity
111
+ @identity ||= sts_client.get_caller_identity
112
+ end
113
+
114
+ def resolve_serial_number(serial_number)
115
+ return serial_number unless serial_number.nil? || serial_number == "automatic"
116
+ user_name = identity.arn.split("/")[1]
117
+ "arn:aws:iam::#{identity.account}:mfa/#{user_name}"
118
+ end
119
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "includes"
4
+ require_relative "../../types"
5
+
6
+ class AwsAssumeRole::Credentials::Providers::SharedKeyringCredentials < ::Aws::SharedCredentials
7
+ include AwsAssumeRole::Logging
8
+ attr_reader :region, :role_arn
9
+
10
+ def initialize(options = {})
11
+ logger.debug "SharedKeyringCredentials initiated with #{options}"
12
+ @path = options[:path]
13
+ @path ||= AwsAssumeRole.shared_config.credentials_path
14
+ @profile_name = options[:profile_name] ||= options[:profile]
15
+ @profile_name ||= ENV["AWS_PROFILE"]
16
+ @profile_name ||= AwsAssumeRole.shared_config.profile_name
17
+ logger.debug "SharedKeyringCredentials resolved profile name #{@profile_name}"
18
+ config = determine_config(@path, @profile_name)
19
+ @role_arn = config.profile_hash(@profile_name)
20
+ @region = config.profile_region(@profile_name)
21
+ @role_arn = config.profile_role(@profile_name)
22
+ attempted_credential = config.credentials(options)
23
+ return unless attempted_credential && attempted_credential.set?
24
+ @credentials = attempted_credential
25
+ end
26
+
27
+ private
28
+
29
+ def determine_config(path, profile_name)
30
+ if path && path == AwsAssumeRole.shared_config.credentials_path
31
+ logger.debug "SharedKeyringCredentials found shared credential path"
32
+ AwsAssumeRole.shared_config
33
+ else
34
+ logger.debug "SharedKeyringCredentials found custom credential path"
35
+ AwsAssumeRole::Store::SharedConfigWithKeyring.new(
36
+ credentials_path: path,
37
+ profile_name: profile_name,
38
+ )
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "i18n"
4
+ require "active_support/json"
5
+ require "active_support/core_ext/object/blank"
6
+ require "active_support/core_ext/string/inflections"
7
+ require "active_support/core_ext/hash/compact"
8
+ require "active_support/core_ext/hash/keys"
9
+ require "active_support/core_ext/hash/slice"
10
+ require "aws-sdk"
11
+ require "aws-sdk-core/ini_parser"
12
+ require "dry-configurable"
13
+ require "dry-struct"
14
+ require "dry-validation"
15
+ require "dry-types"
16
+ require "English"
17
+ require "gli"
18
+ require "highline"
19
+ require "inifile"
20
+ require "json"
21
+ require "keyring"
22
+ require "launchy"
23
+ require "logger"
24
+ require "open-uri"
25
+ require "pastel"
26
+ require "securerandom"
27
+ require "set"
28
+ require "thread"
29
+ require "time"
30
+
31
+ module AwsAssumeRole
32
+ module_function
33
+
34
+ def shared_config
35
+ enabled = ENV["AWS_SDK_CONFIG_OPT_OUT"] ? false : true
36
+ @shared_config ||= SharedConfigWithKeyring.new(config_enabled: enabled)
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "includes"
4
+ require_relative "configuration"
5
+ module AwsAssumeRole::Logging
6
+ module ClassMethods
7
+ def logger
8
+ @logger ||= begin
9
+ logger = Logger.new($stderr)
10
+ logger.level = AwsAssumeRole::Config.log_level
11
+ ENV["GLI_DEBUG"] = "true" if AwsAssumeRole::Config.log_level.zero?
12
+ logger
13
+ end
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def logger
19
+ self.class.logger
20
+ end
21
+ end
22
+
23
+ def self.included(base)
24
+ base.extend ClassMethods
25
+ base.include InstanceMethods
26
+ end
27
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "includes"
4
+ require_relative "logging"
5
+
6
+ class AwsAssumeRole::ProfileConfiguration < Dry::Struct
7
+ constructor_type :schema
8
+ include AwsAssumeRole::Logging
9
+ attribute :access_key_id, Dry::Types["strict.string"].optional
10
+ attribute :credentials, Dry::Types["object"].optional
11
+ attribute :secret_access_key, Dry::Types["strict.string"].optional
12
+ attribute :session_token, Dry::Types["strict.string"].optional
13
+ attribute :duration_seconds, Dry::Types["coercible.int"].optional
14
+ attribute :external_id, Dry::Types["strict.string"].optional
15
+ attribute :path, Dry::Types["strict.string"].optional
16
+ attribute :persist_session, Dry::Types["strict.bool"].optional.default(true)
17
+ attribute :profile, Dry::Types["strict.string"].optional
18
+ attribute :region, Dry::Types["strict.string"].optional
19
+ attribute :role_arn, Dry::Types["strict.string"].optional
20
+ attribute :role_session_name, Dry::Types["strict.string"].optional
21
+ attribute :serial_number, Dry::Types["strict.string"].optional
22
+ attribute :mfa_serial, Dry::Types["strict.string"].optional
23
+ attribute :yubikey_oath_name, Dry::Types["strict.string"].optional
24
+ attribute :use_mfa, Dry::Types["strict.bool"].optional.default(false)
25
+ attribute :no_profile, Dry::Types["strict.bool"].optional.default(false)
26
+ attribute :shell_type, Dry::Types["strict.string"].optional
27
+ attribute :source_profile, Dry::Types["strict.string"].optional
28
+ attribute :args, Dry::Types["strict.array"].optional.default([])
29
+ attribute :instance_profile_credentials_retries, Dry::Types["strict.int"].default(0)
30
+ attribute :instance_profile_credentials_timeout, Dry::Types["coercible.float"].default(1.0)
31
+
32
+ attr_writer :credentials
33
+
34
+ def self.merge_mfa_variable(options)
35
+ new_hash = options.key?(:mfa_serial) ? options.merge(serial_number: options[:mfa_serial]) : options
36
+ new_hash[:use_mfa] ||= new_hash.fetch(:serial_number, nil) ? true : false
37
+ if new_hash.key?(:serial_number) && new_hash[:serial_number] == "automatic"
38
+ new_hash.delete(:serial_number)
39
+ end
40
+ new_hash
41
+ end
42
+
43
+ def self.new_from_cli(global_options, options, args)
44
+ options = global_options.merge options
45
+ options = options.map do |k, v|
46
+ [k.to_s.underscore.to_sym, v]
47
+ end.to_h
48
+ options[:args] = args
49
+ new merge_mfa_variable(options)
50
+ end
51
+
52
+ def self.new_from_credential_provider_initialization(options)
53
+ logger.debug "new_from_credential_provider_initialization with #{options.to_h}"
54
+ new_from_credential_provider(options, credentials: nil, delete: [])
55
+ end
56
+
57
+ def self.new_from_credential_provider(options = {}, credentials: nil, delete: [])
58
+ option_hash = options.to_h
59
+ config = option_hash.fetch(:config, {}).to_h
60
+ hash_to_merge = option_hash.merge config
61
+ hash_to_merge.merge(credentials: credentials) if credentials
62
+ delete.each do |k|
63
+ hash_to_merge.delete k
64
+ end
65
+ hash = merge_mfa_variable(hash_to_merge)
66
+ logger.debug "new_from_credential_provider with #{hash}"
67
+ new hash
68
+ end
69
+
70
+ def to_h
71
+ to_hash
72
+ end
73
+ end