awskeyring 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 45a2721acc4cc5f198c2bf55ee253dcb9f519093
4
+ data.tar.gz: 6ea5cdc339047ba1e057a46a8733d90ea5af77da
5
+ SHA512:
6
+ metadata.gz: 0ae669db134da8e9ba7ecff535bb3e3410e6f314358a818a925761e500f3eac220f63bd740ec72906c83040357dd324495c307550f30b91a4f7aab61834a4eee
7
+ data.tar.gz: 6b005ea78e4fb94f21566edc1c702d75c8e5f2308b21f8bac6fd02329332e0dbd71394d301bf2852c6f82aa2df42dc7b7ffbb6229fdd21ca9a618e90f7a15f04
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /bin/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,22 @@
1
+ Metrics/AbcSize:
2
+ Max: 31
3
+
4
+ Metrics/LineLength:
5
+ Max: 120
6
+
7
+ Metrics/ClassLength:
8
+ Exclude:
9
+ - lib/awskeyring_command.rb
10
+
11
+ Metrics/MethodLength:
12
+ Max: 16
13
+
14
+ Naming/FileName:
15
+ Exclude:
16
+ - Gemfile
17
+ - Rakefile
18
+ - aws-keychain-util.gemspec
19
+
20
+ AllCops:
21
+ Exclude:
22
+ - bin/*
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ before_install: gem install bundler -v 1.11.2
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at tristanmorgan@users.noreply.github.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in awskeyring.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Tristan Morgan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Awskeyring
2
+
3
+ Awskeyring is a small tool to manage AWS account keys in the macOS Keychain.
4
+
5
+ ## Installation
6
+
7
+ Install it with:
8
+
9
+ $ gem install awskeyring
10
+
11
+ ## Usage
12
+
13
+ The CLI is using [Thor](http://whatisthor.com) with help provided interactivly.
14
+
15
+ Commands:
16
+ awskeyring --version, -v # Prints the version
17
+ awskeyring add ACCOUNT # Adds an ACCOUNT to the keyring
18
+ awskeyring add-role ROLE # Adds a ROLE to the keyring
19
+ awskeyring console ACCOUNT # Open the AWS Console for the ACCOUNT
20
+ awskeyring env ACCOUNT # Outputs bourne shell environment exports for an ACCOUNT
21
+ awskeyring help [COMMAND] # Describe available commands or one specific command
22
+ awskeyring initialise # Initialises a new KEYCHAIN
23
+ awskeyring list # Prints a list of accounts in the keyring
24
+ awskeyring list-role # Prints a list of roles in the keyring
25
+ awskeyring remove ACCOUNT # Removes an ACCOUNT from the keyring
26
+ awskeyring remove-role ROLE # Removes a ROLE from the keyring
27
+ awskeyring token ACCOUNT [ROLE] [MFA] # Create an STS Token from a ROLE or an MFA code
28
+
29
+ and autocomplete that can be installed with:
30
+
31
+ $ complete -C /usr/local/bin/aws-creds aws-creds
32
+
33
+ To set your environment easily the following function helps:
34
+
35
+ awsenv() { eval "$(awskeyring env $1)"; }
36
+
37
+ ## Development
38
+
39
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. Run `bundle exec awskeyring` to use the gem in this directory, ignoring other installed copies of this gem.
40
+
41
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
42
+
43
+ ## Contributing
44
+
45
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tristanmorgan/awskeyring. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
46
+
47
+
48
+ ## License
49
+
50
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
51
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ RuboCop::RakeTask.new do |rubocop|
6
+ rubocop.options = ['-D']
7
+ end
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task default: %i[rubocop spec]
@@ -0,0 +1,30 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'awskeyring/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'awskeyring'
7
+ spec.version = Awskeyring::VERSION
8
+ spec.authors = ['Tristan Morgan']
9
+ spec.email = ['tristanmorgan@users.noreply.github.com']
10
+
11
+ spec.summary = 'Manages AWS credentials in the OS X keychain'
12
+ spec.description = 'Manages AWS credentials in the OS X keychain'
13
+ spec.homepage = 'https://github.com/tristanmorgan/awskeyring'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'exe'
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency('aws-sdk-iam')
22
+ spec.add_dependency('highline')
23
+ spec.add_dependency('ruby-keychain')
24
+ spec.add_dependency('thor')
25
+
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'rspec'
29
+ spec.add_development_dependency 'rubocop'
30
+ end
data/exe/awskeyring ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/awskeyring_command'
4
+
5
+ trap('SIGINT') do
6
+ warn "\nExecution aborted.\n"
7
+ exit 1
8
+ end
9
+
10
+ AwskeyringCommand.start
@@ -0,0 +1,3 @@
1
+ module Awskeyring
2
+ VERSION = '0.0.1'.freeze
3
+ end
data/lib/awskeyring.rb ADDED
@@ -0,0 +1,127 @@
1
+ require 'keychain'
2
+ require 'aws-sdk-iam'
3
+
4
+ require 'awskeyring/version'
5
+
6
+ # Aws Key-ring logical object,
7
+ # gives you an interface to access keychains and items.
8
+ module Awskeyring
9
+ PREFS_FILE = (File.expand_path '~/.awskeyring').freeze
10
+ ROLE_PREFIX = 'role '.freeze
11
+ ACCOUNT_PREFIX = 'account '.freeze
12
+
13
+ def self.prefs
14
+ if File.exist? PREFS_FILE
15
+ JSON.parse(File.read(PREFS_FILE))
16
+ else
17
+ {}
18
+ end
19
+ end
20
+
21
+ def self.init_keychain(awskeyring:)
22
+ keychain = Keychain.create(awskeyring)
23
+ keychain.lock_interval = 300
24
+ keychain.lock_on_sleep = true
25
+
26
+ prefs = { awskeyring: awskeyring }
27
+ File.new(Awskeyring::PREFS_FILE, 'w').write JSON.dump(prefs)
28
+ end
29
+
30
+ def self.load_keychain
31
+ unless File.exist?(Awskeyring::PREFS_FILE) || prefs.empty?
32
+ warn "Config missing, run `#{$PROGRAM_NAME} initialise` to recreate."
33
+ exit 1
34
+ end
35
+
36
+ keychain = Keychain.open(prefs['awskeyring'])
37
+ if keychain && keychain.lock_interval > 300
38
+ warn 'It is STRONGLY reccomended to set your keychain to lock in 5 minutes or less.'
39
+ end
40
+ keychain
41
+ end
42
+
43
+ def self.list_items
44
+ items = all_items.all.sort do |a, b|
45
+ a.attributes[:label] <=> b.attributes[:label]
46
+ end
47
+ items.select { |elem| elem.attributes[:label].start_with?(ACCOUNT_PREFIX) }
48
+ end
49
+
50
+ def self.list_roles
51
+ items = all_items.all.sort do |a, b|
52
+ a.attributes[:label] <=> b.attributes[:label]
53
+ end
54
+ items.select { |elem| elem.attributes[:label].start_with?(ROLE_PREFIX) }
55
+ end
56
+
57
+ def self.all_items
58
+ load_keychain.generic_passwords
59
+ end
60
+
61
+ def self.add_item(account:, key:, secret:, comment:)
62
+ all_items.create(
63
+ label: "#{ACCOUNT_PREFIX}#{account}",
64
+ account: key,
65
+ password: secret,
66
+ comment: comment
67
+ )
68
+ end
69
+
70
+ def self.add_role(role:, arn:, account:)
71
+ all_items.create(
72
+ label: "#{ROLE_PREFIX}#{role}",
73
+ account: arn,
74
+ password: '',
75
+ comment: account
76
+ )
77
+ end
78
+
79
+ def self.add_pair(account:, key:, secret:, token:, expiry:, role:)
80
+ all_items.create(label: "session-key #{account}",
81
+ account: key,
82
+ password: secret,
83
+ comment: "#{ROLE_PREFIX}#{role}")
84
+ all_items.create(label: "session-token #{account}",
85
+ account: expiry,
86
+ password: token,
87
+ comment: "#{ROLE_PREFIX}#{role}")
88
+ end
89
+
90
+ def self.get_item(account)
91
+ all_items.where(label: "#{ACCOUNT_PREFIX}#{account}").first
92
+ end
93
+
94
+ def self.get_role(name)
95
+ all_items.where(label: "#{ROLE_PREFIX}#{name}").first
96
+ end
97
+
98
+ def self.get_pair(account)
99
+ session_key = all_items.where(label: "session-key #{account}").first
100
+ session_token = all_items.where(label: "session-token #{account}").first if session_key
101
+ [session_key, session_token]
102
+ end
103
+
104
+ def self.list_item_names
105
+ list_items.map { |elem| elem.attributes[:label][(ACCOUNT_PREFIX.length)..-1] }
106
+ end
107
+
108
+ def self.list_role_names
109
+ list_roles.map { |elem| elem.attributes[:label][(ROLE_PREFIX.length)..-1] }
110
+ end
111
+
112
+ def self.delete_expired(key, token)
113
+ expires_at = Time.at(token.attributes[:account].to_i)
114
+ if expires_at < Time.now
115
+ delete_pair(key, token, '# Removing expired session credentials')
116
+ key = nil
117
+ token = nil
118
+ end
119
+ [key, token]
120
+ end
121
+
122
+ def self.delete_pair(key, token, message)
123
+ puts message if message
124
+ token.delete if token
125
+ key.delete if key
126
+ end
127
+ end
@@ -0,0 +1,314 @@
1
+ require 'aws-sdk-iam'
2
+ require 'cgi'
3
+ require 'highline'
4
+ require 'json'
5
+ require 'open-uri'
6
+ require 'thor'
7
+
8
+ require_relative 'awskeyring'
9
+
10
+ # AWS Key-ring command line interface.
11
+ class AwskeyringCommand < Thor
12
+ map %w[--version -v] => :__version
13
+ map ['ls'] => :list
14
+ map ['lsr'] => :list_role
15
+ map ['rm'] => :remove
16
+ map ['rmr'] => :remove_role
17
+
18
+ desc '--version, -v', 'Prints the version'
19
+ def __version
20
+ puts Awskeyring::VERSION
21
+ end
22
+
23
+ desc 'initialise', 'Initialises a new KEYCHAIN'
24
+ method_option :keychain, type: :string, aliases: '-n', desc: 'Name of KEYCHAIN to initialise.'
25
+ def initialise
26
+ unless Awskeyring.prefs.empty?
27
+ puts "#{Awskeyring::PREFS_FILE} exists. no need to initialise."
28
+ exit 1
29
+ end
30
+
31
+ keychain ||= options[:keychain]
32
+ keychain ||= ask(message: "Name for new keychain (default: 'awskeyring')")
33
+ keychain = 'awskeyring' if keychain.empty?
34
+
35
+ puts 'Creating a new Keychain, you will be prompted for a password for it.'
36
+ Awskeyring.init_keychain(awskeyring: keychain)
37
+
38
+ puts 'Your keychain has been initialised. It will auto-lock after 5 minutes'
39
+ puts 'and when sleeping. Use Keychain Access to adjust.'
40
+ puts
41
+ puts "Add accounts to your #{keychain} keychain with:"
42
+ puts " #{$PROGRAM_NAME} add"
43
+ end
44
+
45
+ desc 'list', 'Prints a list of accounts in the keyring'
46
+ def list
47
+ puts Awskeyring.list_item_names.join("\n")
48
+ end
49
+
50
+ map 'list-role' => :list_role
51
+ desc 'list-role', 'Prints a list of roles in the keyring'
52
+ def list_role
53
+ puts Awskeyring.list_role_names.join("\n")
54
+ end
55
+
56
+ desc 'env ACCOUNT', 'Outputs bourne shell environment exports for an ACCOUNT'
57
+ def env(account = nil)
58
+ account ||= ask(message: 'account name')
59
+ cred, temp_cred = get_valid_item_pair(account: account)
60
+ token = temp_cred.password unless temp_cred.nil?
61
+ put_env_string(
62
+ account: cred.attributes[:label],
63
+ key: cred.attributes[:account],
64
+ secret: cred.password,
65
+ token: token
66
+ )
67
+ end
68
+
69
+ desc 'add ACCOUNT', 'Adds an ACCOUNT to the keyring'
70
+ method_option :key, type: :string, aliases: '-k', desc: 'AWS account key id.'
71
+ method_option :secret, type: :string, aliases: '-s', desc: 'AWS account secret.'
72
+ method_option :mfa, type: :string, aliases: '-m', desc: 'AWS virtual mfa arn.'
73
+ def add(account = nil)
74
+ account ||= ask(message: 'account name')
75
+ key ||= options[:key]
76
+ key ||= ask(message: 'access key id')
77
+ secret ||= options[:secret]
78
+ secret ||= ask(message: 'secret access key', secure: true)
79
+ mfa ||= options[:mfa]
80
+ mfa ||= ask(message: 'mfa arn', optional: true)
81
+
82
+ Awskeyring.add_item(
83
+ account: account,
84
+ key: key,
85
+ secret: secret,
86
+ comment: mfa
87
+ )
88
+ end
89
+
90
+ map 'add-role' => :add_role
91
+ desc 'add-role ROLE', 'Adds a ROLE to the keyring'
92
+ method_option :arn, type: :string, aliases: '-a', desc: 'AWS role arn.'
93
+ def add_role(role = nil)
94
+ role ||= ask(message: 'role name')
95
+ arn ||= options[:arn]
96
+ arn ||= ask(message: 'role arn')
97
+ account ||= ask(message: 'account', optional: true)
98
+
99
+ Awskeyring.add_role(
100
+ role: role,
101
+ arn: arn,
102
+ account: account
103
+ )
104
+ end
105
+
106
+ desc 'remove ACCOUNT', 'Removes an ACCOUNT from the keyring'
107
+ def remove(account = nil)
108
+ account ||= ask(message: 'account name')
109
+ cred, temp_cred = get_valid_item_pair(account: account)
110
+ Awskeyring.delete_pair(cred, temp_cred, "# Removing account #{account}")
111
+ end
112
+
113
+ map 'remove-role' => :remove_role
114
+ desc 'remove-role ROLE', 'Removes a ROLE from the keyring'
115
+ def remove_role(role = nil)
116
+ role ||= ask(message: 'role name')
117
+ item_role = Awskeyring.get_role(role)
118
+ Awskeyring.delete_pair(item_role, nil, "# Removing role #{role}")
119
+ end
120
+
121
+ desc 'token ACCOUNT [ROLE] [MFA]', 'Create an STS Token from a ROLE or an MFA code'
122
+ method_option :role, type: :string, aliases: '-r', desc: 'The ROLE to assume.'
123
+ method_option :code, type: :string, aliases: '-c', desc: 'Virtual mfa CODE.'
124
+ method_option :duration, type: :string, aliases: '-d', desc: 'Session DURATION in seconds.'
125
+ def token(account = nil, role = nil, code = nil)
126
+ account ||= ask(message: 'account name')
127
+ role ||= options[:role]
128
+ code ||= options[:code]
129
+ duration = options[:duration]
130
+ duration ||= (60 * 60 * 1).to_s if role
131
+ duration ||= (60 * 60 * 12).to_s if code
132
+
133
+ if !role && !code
134
+ warn 'Please use either a role or a code'
135
+ exit 2
136
+ end
137
+
138
+ session_key, session_token = Awskeyring.get_pair(account)
139
+ Awskeyring.delete_pair(session_key, session_token, '# Removing STS credentials') if session_key
140
+
141
+ item = Awskeyring.get_item(account)
142
+ item_role = Awskeyring.get_role(role) if role
143
+
144
+ sts = Aws::STS::Client.new(access_key_id: item.attributes[:account], secret_access_key: item.password)
145
+
146
+ begin
147
+ response =
148
+ if code && role
149
+ sts.assume_role(
150
+ duration_seconds: duration.to_i,
151
+ role_arn: item_role.attributes[:account],
152
+ role_session_name: ENV['USER'],
153
+ serial_number: item.attributes[:comment],
154
+ token_code: code
155
+ )
156
+ elsif role
157
+ sts.assume_role(
158
+ duration_seconds: duration.to_i,
159
+ role_arn: item_role.attributes[:account],
160
+ role_session_name: ENV['USER']
161
+ )
162
+ elsif code
163
+ sts.get_session_token(
164
+ duration_seconds: duration.to_i,
165
+ serial_number: item.attributes[:comment],
166
+ token_code: code
167
+ )
168
+ end
169
+ rescue Aws::STS::Errors::AccessDenied => e
170
+ puts e.to_s
171
+ exit 1
172
+ end
173
+
174
+ Awskeyring.add_pair(
175
+ account: account,
176
+ key: response.credentials[:access_key_id],
177
+ secret: response.credentials[:secret_access_key],
178
+ token: response.credentials[:session_token],
179
+ expiry: response.credentials[:expiration].to_i.to_s,
180
+ role: role
181
+ )
182
+
183
+ puts "Authentication valid until #{response.credentials[:expiration]}"
184
+ end
185
+
186
+ desc 'console ACCOUNT', 'Open the AWS Console for the ACCOUNT'
187
+ def console(account = nil)
188
+ account ||= ask(message: 'account name')
189
+ cred, temp_cred = get_valid_item_pair(account: account)
190
+ token = temp_cred.password unless temp_cred.nil?
191
+
192
+ console_url = 'https://console.aws.amazon.com/console/home'
193
+ signin_url = 'https://signin.aws.amazon.com/federation'
194
+ policy_json = {
195
+ Version: '2012-10-17',
196
+ Statement: [{
197
+ Action: '*',
198
+ Resource: '*',
199
+ Effect: 'Allow'
200
+ }]
201
+ }.to_json
202
+
203
+ if temp_cred
204
+ session_json = {
205
+ sessionId: cred.attributes[:account],
206
+ sessionKey: cred.password,
207
+ sessionToken: token
208
+ }.to_json
209
+ else
210
+ sts = Aws::STS::Client.new(access_key_id: cred.attributes[:account],
211
+ secret_access_key: cred.password)
212
+
213
+ session = sts.get_federation_token(name: ENV['USER'],
214
+ policy: policy_json,
215
+ duration_seconds: (60 * 60 * 12))
216
+ session_json = {
217
+ sessionId: session.credentials[:access_key_id],
218
+ sessionKey: session.credentials[:secret_access_key],
219
+ sessionToken: session.credentials[:session_token]
220
+ }.to_json
221
+
222
+ end
223
+ get_signin_token_url = signin_url + '?Action=getSigninToken' \
224
+ '&Session=' + CGI.escape(session_json)
225
+
226
+ returned_content = open(get_signin_token_url).read
227
+
228
+ signin_token = JSON.parse(returned_content)['SigninToken']
229
+ signin_token_param = '&SigninToken=' + CGI.escape(signin_token)
230
+ destination_param = '&Destination=' + CGI.escape(console_url)
231
+
232
+ login_url = signin_url + '?Action=login' + signin_token_param + destination_param
233
+
234
+ pid = spawn("open \"#{login_url}\"")
235
+ Process.wait pid
236
+ end
237
+
238
+ # autocomplete
239
+ desc 'awskeyring CURR PREV', 'Autocompletion for bourne shells', hide: true
240
+ def awskeyring(curr, prev)
241
+ comp_line = ENV['COMP_LINE']
242
+ unless comp_line
243
+ warn "enable autocomplete with 'complete -C /path-to-command/awskeyring awskeyring'"
244
+ exit 1
245
+ end
246
+ comp_len = comp_line.split.length
247
+ comp_len += 1 if curr == ''
248
+
249
+ case comp_len
250
+ when 2
251
+ puts list_commands.select { |elem| elem.start_with?(curr) }.join("\n")
252
+ when 3
253
+ if prev == 'help'
254
+ puts list_commands.select { |elem| elem.start_with?(curr) }.join("\n")
255
+ elsif prev == 'remove-role'
256
+ puts Awskeyring.list_role_names.select { |elem| elem.start_with?(curr) }.join("\n")
257
+ else
258
+ puts Awskeyring.list_item_names.select { |elem| elem.start_with?(curr) }.join("\n")
259
+ end
260
+ when 4
261
+ puts Awskeyring.list_role_names.select { |elem| elem.start_with?(curr) }.join("\n")
262
+ else
263
+ exit 1
264
+ end
265
+ end
266
+
267
+ private
268
+
269
+ def list_commands
270
+ self.class.all_commands.keys.map { |elem| elem.tr('_', '-') }
271
+ end
272
+
273
+ def get_valid_item_pair(account:)
274
+ session_key, session_token = Awskeyring.get_pair(account)
275
+ session_key, session_token = Awskeyring.delete_expired(session_key, session_token) if session_key
276
+
277
+ if session_key && session_token
278
+ puts '# Using temporary session credentials'
279
+ return session_key, session_token
280
+ end
281
+
282
+ item = Awskeyring.get_item(account)
283
+ if item.nil?
284
+ warn "# Credential not found with name: #{account}"
285
+ exit 2
286
+ end
287
+ [item, nil]
288
+ end
289
+
290
+ def put_env_string(account:, key:, secret:, token:)
291
+ puts "export AWS_ACCOUNT_NAME=\"#{account}\""
292
+ puts "export AWS_ACCESS_KEY_ID=\"#{key}\""
293
+ puts "export AWS_ACCESS_KEY=\"#{key}\""
294
+ puts "export AWS_SECRET_ACCESS_KEY=\"#{secret}\""
295
+ puts "export AWS_SECRET_KEY=\"#{secret}\""
296
+ if token
297
+ puts "export AWS_SECURITY_TOKEN=\"#{token}\""
298
+ puts "export AWS_SESSION_TOKEN=\"#{token}\""
299
+ else
300
+ puts 'unset AWS_SECURITY_TOKEN'
301
+ puts 'unset AWS_SESSION_TOKEN'
302
+ end
303
+ end
304
+
305
+ def ask(message:, secure: false, optional: false)
306
+ if secure
307
+ HighLine.new.ask(message.rjust(20) + ': ') { |q| q.echo = '*' }
308
+ elsif optional
309
+ HighLine.new.ask((message + ' (optional)').rjust(20) + ': ')
310
+ else
311
+ HighLine.new.ask(message.rjust(20) + ': ')
312
+ end
313
+ end
314
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: awskeyring
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tristan Morgan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-iam
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-keychain
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Manages AWS credentials in the OS X keychain
126
+ email:
127
+ - tristanmorgan@users.noreply.github.com
128
+ executables:
129
+ - awskeyring
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".rubocop.yml"
136
+ - ".travis.yml"
137
+ - CODE_OF_CONDUCT.md
138
+ - Gemfile
139
+ - LICENSE.txt
140
+ - README.md
141
+ - Rakefile
142
+ - awskeyring.gemspec
143
+ - exe/awskeyring
144
+ - lib/awskeyring.rb
145
+ - lib/awskeyring/version.rb
146
+ - lib/awskeyring_command.rb
147
+ homepage: https://github.com/tristanmorgan/awskeyring
148
+ licenses:
149
+ - MIT
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 2.6.12
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: Manages AWS credentials in the OS X keychain
171
+ test_files: []