aws_assume_role 1.0.6-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +21 -0
  7. data/CHANGELOG.md +57 -0
  8. data/Gemfile +18 -0
  9. data/LICENSE.md +201 -0
  10. data/README.md +303 -0
  11. data/Rakefile +47 -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