aws_assume_role 0.0.3 → 0.1.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +31 -11
- data/Gemfile +7 -13
- data/LICENSE.md +201 -19
- data/README.md +176 -145
- data/aws_assume_role.gemspec +35 -21
- data/bin/aws-assume-role +1 -83
- data/i18n/en.yml +106 -0
- data/lib/aws_assume_role.rb +2 -3
- data/lib/aws_assume_role/cli.rb +15 -0
- data/lib/aws_assume_role/cli/actions/abstract_action.rb +53 -0
- data/lib/aws_assume_role/cli/actions/configure_profile.rb +21 -0
- data/lib/aws_assume_role/cli/actions/configure_role_assumption.rb +19 -0
- data/lib/aws_assume_role/cli/actions/console.rb +68 -0
- data/lib/aws_assume_role/cli/actions/delete_profile.rb +20 -0
- data/lib/aws_assume_role/cli/actions/includes.rb +18 -0
- data/lib/aws_assume_role/cli/actions/list_profiles.rb +10 -0
- data/lib/aws_assume_role/cli/actions/migrate_profile.rb +18 -0
- data/lib/aws_assume_role/cli/actions/reset_environment.rb +48 -0
- data/lib/aws_assume_role/cli/actions/run.rb +34 -0
- data/lib/aws_assume_role/cli/actions/set_environment.rb +60 -0
- data/lib/aws_assume_role/cli/actions/test.rb +31 -0
- data/lib/aws_assume_role/cli/commands/configure.rb +29 -0
- data/lib/aws_assume_role/cli/commands/console.rb +17 -0
- data/lib/aws_assume_role/cli/commands/delete.rb +11 -0
- data/lib/aws_assume_role/cli/commands/environment.rb +32 -0
- data/lib/aws_assume_role/cli/commands/list.rb +10 -0
- data/lib/aws_assume_role/cli/commands/migrate.rb +11 -0
- data/lib/aws_assume_role/cli/commands/run.rb +17 -0
- data/lib/aws_assume_role/cli/commands/test.rb +18 -0
- data/lib/aws_assume_role/configuration.rb +19 -0
- data/lib/aws_assume_role/core_ext/aws-sdk/credential_provider_chain.rb +2 -0
- data/lib/aws_assume_role/core_ext/aws-sdk/includes.rb +7 -0
- data/lib/aws_assume_role/credentials/factories.rb +9 -0
- data/lib/aws_assume_role/credentials/factories/abstract_factory.rb +31 -0
- data/lib/aws_assume_role/credentials/factories/assume_role.rb +38 -0
- data/lib/aws_assume_role/credentials/factories/default_chain_provider.rb +101 -0
- data/lib/aws_assume_role/credentials/factories/environment.rb +24 -0
- data/lib/aws_assume_role/credentials/factories/includes.rb +17 -0
- data/lib/aws_assume_role/credentials/factories/instance_profile.rb +17 -0
- data/lib/aws_assume_role/credentials/factories/repository.rb +35 -0
- data/lib/aws_assume_role/credentials/factories/shared.rb +15 -0
- data/lib/aws_assume_role/credentials/factories/shared_keyring.rb +16 -0
- data/lib/aws_assume_role/credentials/factories/static.rb +16 -0
- data/lib/aws_assume_role/credentials/providers/assume_role_credentials.rb +58 -0
- data/lib/aws_assume_role/credentials/providers/includes.rb +9 -0
- data/lib/aws_assume_role/credentials/providers/mfa_session_credentials.rb +102 -0
- data/lib/aws_assume_role/credentials/providers/shared_keyring_credentials.rb +22 -0
- data/lib/aws_assume_role/includes.rb +30 -0
- data/lib/aws_assume_role/logging.rb +16 -28
- data/lib/aws_assume_role/profile_configuration.rb +71 -0
- data/lib/aws_assume_role/runner.rb +39 -0
- data/lib/aws_assume_role/store/includes.rb +16 -0
- data/lib/aws_assume_role/store/keyring.rb +59 -0
- data/lib/aws_assume_role/store/serialization.rb +18 -0
- data/lib/aws_assume_role/store/shared_config_with_keyring.rb +175 -0
- data/lib/aws_assume_role/types.rb +30 -0
- data/lib/aws_assume_role/ui.rb +55 -0
- data/lib/aws_assume_role/vendored/aws.rb +4 -0
- data/lib/aws_assume_role/vendored/aws/README.md +2 -0
- data/lib/aws_assume_role/vendored/aws/assume_role_credentials.rb +68 -0
- data/lib/aws_assume_role/vendored/aws/includes.rb +9 -0
- data/lib/aws_assume_role/vendored/aws/refreshing_credentials.rb +60 -0
- data/lib/aws_assume_role/vendored/aws/shared_config.rb +220 -0
- data/lib/aws_assume_role/version.rb +3 -0
- metadata +264 -20
- data/.rspec +0 -2
- data/Rakefile +0 -2
- data/bin/test.rb +0 -39
- data/lib/aws_assume_role/credentials.rb +0 -92
- data/lib/aws_assume_role/profile.rb +0 -203
- data/lib/aws_assume_role/profile/assume_role.rb +0 -127
- data/lib/aws_assume_role/profile/basic.rb +0 -152
- data/lib/aws_assume_role/profile/list.rb +0 -57
@@ -1,203 +0,0 @@
|
|
1
|
-
require 'keyring'
|
2
|
-
|
3
|
-
module AWSAssumeRole
|
4
|
-
|
5
|
-
# Base Profile superclass
|
6
|
-
class Profile
|
7
|
-
|
8
|
-
include Logging
|
9
|
-
|
10
|
-
# Class methods for dispatch to individual Profile strategy
|
11
|
-
|
12
|
-
@implementations = {}
|
13
|
-
@named_profiles = {}
|
14
|
-
@config_file = '-'
|
15
|
-
|
16
|
-
class << self
|
17
|
-
|
18
|
-
attr_accessor :implementations
|
19
|
-
attr_accessor :named_profiles
|
20
|
-
attr_accessor :config_file
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.register_implementation(type, impl)
|
25
|
-
logger.info('Registering implementation ' \
|
26
|
-
"for type '#{type}': #{impl}")
|
27
|
-
AWSAssumeRole::Profile.implementations[type] = impl
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.load_profiles
|
31
|
-
Dir.glob(
|
32
|
-
File.expand_path('profile/*.rb', File.dirname(__FILE__)),
|
33
|
-
).each do |profile_class|
|
34
|
-
require profile_class
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.create(name, options)
|
39
|
-
|
40
|
-
options['type'] = 'basic' unless options.key?('type')
|
41
|
-
|
42
|
-
if implementations.key?(options['type'])
|
43
|
-
logger.info("Creating profile '#{name}' "\
|
44
|
-
"with type #{options['type']}")
|
45
|
-
i = implementations[options['type']].new(name, options)
|
46
|
-
named_profiles[name] = i
|
47
|
-
return i
|
48
|
-
end
|
49
|
-
|
50
|
-
STDERR.puts 'No implementation for profiles of type '\
|
51
|
-
"'#{options['type']}'"
|
52
|
-
exit -1 # rubocop:disable Lint/AmbiguousOperator
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.get_by_name(name)
|
57
|
-
unless named_profiles.key?(name)
|
58
|
-
STDERR.puts "No profile '#{name}' found"
|
59
|
-
exit -1 # rubocop:disable Lint/AmbiguousOperator
|
60
|
-
end
|
61
|
-
named_profiles[name]
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.load_config_file(config_path)
|
65
|
-
@config_file = config_path
|
66
|
-
logger.info("Loading configuration from #{config_path}")
|
67
|
-
parse_config(File.open(config_path))
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.parse_config(yaml)
|
71
|
-
|
72
|
-
require 'yaml'
|
73
|
-
|
74
|
-
profiles = YAML.load(yaml)
|
75
|
-
profiles.each do |name, options|
|
76
|
-
options['config_file'] = config_file
|
77
|
-
options['name'] = name
|
78
|
-
AWSAssumeRole::Profile.create(name, options)
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
# Superclass for Profile strategies
|
84
|
-
|
85
|
-
def set_env(prefix = '') # rubocop:disable Style/AccessorMethodName
|
86
|
-
|
87
|
-
logger.info("Setting environment with prefix '#{prefix}'")
|
88
|
-
|
89
|
-
ENV["#{prefix}AWS_ACCESS_KEY_ID"] = access_key_id
|
90
|
-
ENV["#{prefix}AWS_SECRET_ACCESS_KEY"] = secret_access_key
|
91
|
-
ENV["#{prefix}AWS_DEFAULT_REGION"] = region
|
92
|
-
|
93
|
-
return unless respond_to?(:session_token)
|
94
|
-
|
95
|
-
logger.info(':session_token available, setting environment')
|
96
|
-
ENV["#{prefix}AWS_SESSION_TOKEN"] = session_token
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
def access_key_id
|
101
|
-
raise NotImplementedError
|
102
|
-
end
|
103
|
-
|
104
|
-
def secret_access_key
|
105
|
-
raise NotImplementedError
|
106
|
-
end
|
107
|
-
|
108
|
-
def region
|
109
|
-
raise NotImplementedError
|
110
|
-
end
|
111
|
-
|
112
|
-
def sts_client
|
113
|
-
raise NotImplementedError
|
114
|
-
end
|
115
|
-
|
116
|
-
attr_reader :name
|
117
|
-
|
118
|
-
def use
|
119
|
-
raise NotImplementedError
|
120
|
-
end
|
121
|
-
|
122
|
-
def add
|
123
|
-
raise NotImplementedError
|
124
|
-
end
|
125
|
-
|
126
|
-
def remove
|
127
|
-
raise NotImplementedError
|
128
|
-
end
|
129
|
-
|
130
|
-
def token_code
|
131
|
-
puts "Enter your MFA's time-based one time password: "
|
132
|
-
token_code = STDIN.gets
|
133
|
-
token_code.chomp!
|
134
|
-
end
|
135
|
-
|
136
|
-
def keyring_key
|
137
|
-
"#{@options['config_file']}|#{@options['name']}"
|
138
|
-
end
|
139
|
-
|
140
|
-
def session(duration = 3600)
|
141
|
-
|
142
|
-
# See if we already have a non-expired session cached in this
|
143
|
-
# object.
|
144
|
-
|
145
|
-
unless @session.nil?
|
146
|
-
|
147
|
-
logger.info('Found session cached in object')
|
148
|
-
return @session unless @session.expired?
|
149
|
-
logger.info('Session expired, deleting keyring key '\
|
150
|
-
"'#{keyring_key}'")
|
151
|
-
@session.delete_from_keyring(keyring_key)
|
152
|
-
|
153
|
-
end
|
154
|
-
|
155
|
-
# See if there's a non-exipred session cached in the keyring
|
156
|
-
|
157
|
-
@session = AWSAssumeRole::Credentials.load_from_keyring(keyring_key)
|
158
|
-
|
159
|
-
unless @session.nil?
|
160
|
-
|
161
|
-
logger.info("Found session in keyring for '#{keyring_key}'")
|
162
|
-
return @session unless @session.expired?
|
163
|
-
logger.info('Session expired, deleting keyring key '\
|
164
|
-
"'#{keyring_key}'")
|
165
|
-
@session.delete_from_keyring(keyring_key)
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
begin
|
170
|
-
identity = sts_client.get_caller_identity
|
171
|
-
rescue Aws::Errors::MissingCredentialsError
|
172
|
-
STDERR.puts "No credentials found. Set one in the credentials file "\
|
173
|
-
"or environment."
|
174
|
-
exit -1 # rubocop:disable Lint/AmbiguousOperator
|
175
|
-
end
|
176
|
-
user_name = identity.arn.split('/')[1]
|
177
|
-
mfa_arn = "arn:aws:iam::#{identity.account}:mfa/#{user_name}"
|
178
|
-
|
179
|
-
mfa_token_code = token_code
|
180
|
-
|
181
|
-
begin
|
182
|
-
session = sts_client.get_session_token(
|
183
|
-
duration_seconds: duration,
|
184
|
-
serial_number: mfa_arn,
|
185
|
-
token_code: mfa_token_code,
|
186
|
-
)
|
187
|
-
rescue Aws::STS::Errors::AccessDenied
|
188
|
-
STDERR.puts 'MultiFactorAuthentication failed with invalid MFA ' \
|
189
|
-
'one time pass code.'
|
190
|
-
exit -1 # rubocop:disable Lint/AmbiguousOperator
|
191
|
-
end
|
192
|
-
|
193
|
-
@session = Credentials.create_from_sdk(session.credentials)
|
194
|
-
logger.info("Storing session in keyring '#{keyring_key}'")
|
195
|
-
@session.store_in_keyring(keyring_key)
|
196
|
-
|
197
|
-
@session
|
198
|
-
|
199
|
-
end
|
200
|
-
|
201
|
-
end
|
202
|
-
|
203
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
# AWSAssumeRole
|
2
|
-
module AWSAssumeRole
|
3
|
-
|
4
|
-
class Profile
|
5
|
-
|
6
|
-
# A Profile implementation for assuming roles using STS
|
7
|
-
class AssumeRole < Profile
|
8
|
-
|
9
|
-
include Logging
|
10
|
-
|
11
|
-
register_implementation('assume_role', self)
|
12
|
-
|
13
|
-
@sts_client = nil
|
14
|
-
@role = nil
|
15
|
-
@options = nil
|
16
|
-
@name = nil
|
17
|
-
|
18
|
-
def default_options
|
19
|
-
{
|
20
|
-
'parent' => 'default',
|
21
|
-
'duration' => 3600,
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(name, options = {})
|
26
|
-
|
27
|
-
require 'aws-sdk'
|
28
|
-
|
29
|
-
@options = default_options.merge(options)
|
30
|
-
@name = name
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
def sts_client
|
35
|
-
|
36
|
-
return @sts_client unless @sts_client.nil?
|
37
|
-
|
38
|
-
parent = AWSAssumeRole::Profile.get_by_name(@options['parent'])
|
39
|
-
|
40
|
-
@sts_client = Aws::STS::Client.new(
|
41
|
-
access_key_id: parent.session.access_key_id,
|
42
|
-
secret_access_key: parent.session.secret_access_key,
|
43
|
-
session_token: parent.session.session_token,
|
44
|
-
)
|
45
|
-
|
46
|
-
@sts_client
|
47
|
-
|
48
|
-
rescue Aws::Errors::MissingRegionError
|
49
|
-
|
50
|
-
STDERR.puts 'No region was given. \
|
51
|
-
Set one in the credentials file or environment'
|
52
|
-
exit -1 # rubocop:disable Lint/AmbiguousOperator
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def role_credentials
|
57
|
-
|
58
|
-
# Check for non-expired session cached here
|
59
|
-
|
60
|
-
unless @role_credentials.nil?
|
61
|
-
|
62
|
-
return @role_credentials unless @role_credentials.expired?
|
63
|
-
@role_credentials.delete_from_keyring(keyring_key)
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
# See if here's a non-exipred session in the keyring
|
68
|
-
|
69
|
-
@role_credentials = Credentials.load_from_keyring(keyring_key)
|
70
|
-
|
71
|
-
unless @role_credentials.nil?
|
72
|
-
|
73
|
-
return @role_credentials unless @role_credentials.expired?
|
74
|
-
@role_credentials.delete_from_keyring(keyring_key)
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
role = sts_client.assume_role(
|
79
|
-
role_arn: @options['role_arn'],
|
80
|
-
role_session_name: name, # use something else?
|
81
|
-
duration_seconds: @options['duration'],
|
82
|
-
)
|
83
|
-
|
84
|
-
@role_credentials =
|
85
|
-
Credentials.create_from_sdk(role.credentials)
|
86
|
-
|
87
|
-
@role_credentials.store_in_keyring(keyring_key)
|
88
|
-
|
89
|
-
@role_credentials
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
|
-
def access_key_id
|
94
|
-
role_credentials.access_key_id
|
95
|
-
end
|
96
|
-
|
97
|
-
def secret_access_key
|
98
|
-
role_credentials.secret_access_key
|
99
|
-
end
|
100
|
-
|
101
|
-
def session_token
|
102
|
-
role_credentials.session_token
|
103
|
-
end
|
104
|
-
|
105
|
-
def region
|
106
|
-
@options['region']
|
107
|
-
end
|
108
|
-
|
109
|
-
def use
|
110
|
-
set_env if @options['set_environment']
|
111
|
-
end
|
112
|
-
|
113
|
-
def remove
|
114
|
-
Credentials.new({}).delete_from_keyring(keyring_key)
|
115
|
-
end
|
116
|
-
|
117
|
-
def add
|
118
|
-
STDERR.puts "You can't add credentials to an assume_role "\
|
119
|
-
'just basic/parent accounts.'
|
120
|
-
exit -1 # rubocop:disable Lint/AmbiguousOperator
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
end
|
126
|
-
|
127
|
-
end
|
@@ -1,152 +0,0 @@
|
|
1
|
-
# AWSAssumeRole
|
2
|
-
module AWSAssumeRole
|
3
|
-
|
4
|
-
class Profile
|
5
|
-
|
6
|
-
# Profile implementation which takes credentials from either
|
7
|
-
# passed options, from the environment, or from .aws/credentials
|
8
|
-
# file (per the standard behaviour of Aws::STS::Client)
|
9
|
-
class Basic < Profile
|
10
|
-
|
11
|
-
include Logging
|
12
|
-
|
13
|
-
register_implementation('basic', self)
|
14
|
-
|
15
|
-
@sts_client = nil
|
16
|
-
@options = nil
|
17
|
-
@name = nil
|
18
|
-
|
19
|
-
def initialize(name, options = {})
|
20
|
-
|
21
|
-
require 'aws-sdk'
|
22
|
-
|
23
|
-
@options = options
|
24
|
-
@name = name
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
def sts_client
|
29
|
-
logger.debug("Calling STS client")
|
30
|
-
|
31
|
-
return @sts_client unless @sts_client.nil?
|
32
|
-
|
33
|
-
if @options.key?('profile')
|
34
|
-
|
35
|
-
logger.info("Loading profile #{@options['profile']} from ~/.aws/credentials")
|
36
|
-
# Attempt to load with profile name suplied
|
37
|
-
@sts_client = Aws::STS::Client.new(
|
38
|
-
profile: @options['profile'],
|
39
|
-
)
|
40
|
-
|
41
|
-
elsif access_key_id && secret_access_key
|
42
|
-
|
43
|
-
logger.debug("Access Key: #{access_key_id}")
|
44
|
-
logger.debug("Secret Key: #{secret_access_key}")
|
45
|
-
logger.debug("Region: #{region}")
|
46
|
-
|
47
|
-
@sts_client = Aws::STS::Client.new(
|
48
|
-
access_key_id: access_key_id,
|
49
|
-
secret_access_key: secret_access_key,
|
50
|
-
region: region,
|
51
|
-
)
|
52
|
-
|
53
|
-
else
|
54
|
-
|
55
|
-
@sts_client = Aws::STS::Client.new
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
@sts_client
|
60
|
-
|
61
|
-
rescue Aws::Errors::MissingRegionError
|
62
|
-
|
63
|
-
STDERR.puts 'No region was given. \
|
64
|
-
Set one in the credentials file or environment'
|
65
|
-
exit -1 # rubocop:disable Lint/AmbiguousOperator
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
def basic_credentials
|
70
|
-
logger.debug("Loading basic credentials")
|
71
|
-
# Check for profile creds in keyring first
|
72
|
-
@basic_credentials = Credentials.load_from_keyring("#{keyring_key}|basic")
|
73
|
-
|
74
|
-
return @basic_credentials unless @basic_credentials.nil?
|
75
|
-
|
76
|
-
creds = {
|
77
|
-
:access_key_id => ENV['AWS_ACCESS_KEY_ID'],
|
78
|
-
:secret_access_key => ENV['AWS_SECRET_ACCESS_KEY'],
|
79
|
-
}
|
80
|
-
|
81
|
-
if creds[:access_key_id].nil? or creds[:secret_access_key].nil?
|
82
|
-
STDERR.puts 'No AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY ' \
|
83
|
-
'found in the environment.'
|
84
|
-
exit -1 # rubocop:disable Lint/AmbiguousOperator
|
85
|
-
end
|
86
|
-
|
87
|
-
unless @options['region'].nil?
|
88
|
-
creds[:region] = @options['region']
|
89
|
-
else
|
90
|
-
creds[:region] = ENV['AWS_DEFAULT_REGION']
|
91
|
-
end
|
92
|
-
|
93
|
-
@basic_credentials = AWSAssumeRole::Credentials.new(creds)
|
94
|
-
|
95
|
-
@basic_credentials
|
96
|
-
end
|
97
|
-
|
98
|
-
def access_key_id
|
99
|
-
@options['access_key_id'] || basic_credentials.access_key_id
|
100
|
-
end
|
101
|
-
|
102
|
-
def secret_access_key
|
103
|
-
@options['secret_access_key'] || basic_credentials.secret_access_key
|
104
|
-
end
|
105
|
-
|
106
|
-
def region
|
107
|
-
@options['region'] || basic_credentials.region
|
108
|
-
end
|
109
|
-
|
110
|
-
def remove
|
111
|
-
Credentials.new({}).delete_from_keyring(keyring_key)
|
112
|
-
Credentials.new({}).delete_from_keyring("#{keyring_key}|basic")
|
113
|
-
end
|
114
|
-
|
115
|
-
def add
|
116
|
-
if @options['profile']
|
117
|
-
puts "WARNING: Storing credentials but a profile is specified and used for #{@name}"
|
118
|
-
end
|
119
|
-
if @options['access_key_id'] or @options['secret_access_key']
|
120
|
-
puts "WARNING: Storing credentials but they are specified in config for #{@name}"
|
121
|
-
end
|
122
|
-
|
123
|
-
@basic_credentials = Credentials.load_from_keyring("#{keyring_key}|basic")
|
124
|
-
new_creds = get_credentials
|
125
|
-
@basic_credentials.delete_from_keyring(keyring_key) unless @basic_credentials.nil?
|
126
|
-
@basic_credentials = AWSAssumeRole::Credentials.new(new_creds)
|
127
|
-
@basic_credentials.store_in_keyring("#{keyring_key}|basic")
|
128
|
-
end
|
129
|
-
|
130
|
-
def get_credentials
|
131
|
-
puts "Enter your AWS_ACCESS_KEY_ID: "
|
132
|
-
id = STDIN.gets
|
133
|
-
id.chomp!
|
134
|
-
puts "Enter your AWS_SECRET_ACCESS_KEY: "
|
135
|
-
secret = STDIN.gets
|
136
|
-
secret.chomp!
|
137
|
-
puts "Enter a AWS Region:"
|
138
|
-
region = STDIN.gets
|
139
|
-
region.chomp!
|
140
|
-
|
141
|
-
creds = {
|
142
|
-
:access_key_id => id,
|
143
|
-
:secret_access_key => secret,
|
144
|
-
:region => region,
|
145
|
-
}
|
146
|
-
creds
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
end
|
151
|
-
|
152
|
-
end
|