credsummoner 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 +7 -0
- data/bin/credsummoner +166 -0
- data/lib/credsummoner.rb +8 -0
- data/lib/credsummoner/account.rb +14 -0
- data/lib/credsummoner/config.rb +46 -0
- data/lib/credsummoner/okta/credentials.rb +12 -0
- data/lib/credsummoner/okta/session.rb +116 -0
- data/lib/credsummoner/okta/user.rb +115 -0
- data/lib/credsummoner/role.rb +25 -0
- data/lib/credsummoner/saml_assertion.rb +32 -0
- data/lib/credsummoner/web.rb +39 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d9c3a614f16fb15c9ba37707b6b87e834b7961573445a1416ea4a6b343310ae6
|
4
|
+
data.tar.gz: d79155dacf952e1b9974f3cfffadbc38619c1b5466c820ab9de3fd2d73c98e6b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 19bac7a5369554246d91ef42005581f1a1321d89929f66e6b6bec89ca15d6501c21d2c1b7b6a999eca9690cea5ee76463444f149f95881fd503c7b688627d587
|
7
|
+
data.tar.gz: f63d654e84c47e50e7e9e6d5bc2cda5651e8d290b8de76d62ea0debb0fc5872f20b2aed343ec90823646853fd5de709957088bd4d10251d52b99e42e75b1aef4
|
data/bin/credsummoner
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'credsummoner'
|
4
|
+
require 'optparse'
|
5
|
+
require 'tty-prompt'
|
6
|
+
|
7
|
+
duration = 12 * 60 * 60 # 12 hour session
|
8
|
+
mode = :command
|
9
|
+
region = ENV['AWS_REGION'] || 'us-east-1'
|
10
|
+
account_name = nil
|
11
|
+
role_name = nil
|
12
|
+
|
13
|
+
root_parser = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: credsummoner SUBCOMMAND [OPTION ...]"
|
15
|
+
#opts.separator ""
|
16
|
+
end
|
17
|
+
|
18
|
+
subcommands = {
|
19
|
+
'get' => OptionParser.new do |opts|
|
20
|
+
opts.banner = "Usage: credsummoner get USERNAME [OPTION ...] [COMMAND ...]
|
21
|
+
|
22
|
+
Fetch temporary AWS tokens and do one of the following:
|
23
|
+
|
24
|
+
* Print environment variables to stdout if --env is specified
|
25
|
+
* Run COMMAND if specified
|
26
|
+
* Otherwise, run default user shell
|
27
|
+
"
|
28
|
+
|
29
|
+
opts.on('-d', '--duration=DURATION', 'the ttl of the session token in seconds') do |d|
|
30
|
+
duration = d
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-e', '--environment', 'display environment variables instead of running a command') do |e|
|
34
|
+
mode = :environment
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('--region=REGION', 'AWS region') do |r|
|
38
|
+
region = r
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on('-a', '--account=ACCOUNT', 'AWS account alias') do |a|
|
42
|
+
account_name = a
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on('-r', '--role=ROLE', 'AWS role name') do |r|
|
46
|
+
role_name = r
|
47
|
+
end
|
48
|
+
end,
|
49
|
+
'config' => OptionParser.new do |opts|
|
50
|
+
opts.banner = 'Usage: credsummoner config KEY VALUE
|
51
|
+
|
52
|
+
Set the configuration option KEY to VALUE.
|
53
|
+
|
54
|
+
Available configuration keys:
|
55
|
+
|
56
|
+
* okta_aws_embed_link: The embed link for the AWS application in Okta.
|
57
|
+
This link can be found in the "General" tab when viewing the AWS
|
58
|
+
application settings in the Okta admin interface.
|
59
|
+
'
|
60
|
+
end
|
61
|
+
}
|
62
|
+
|
63
|
+
root_parser.order!
|
64
|
+
subcommand = ARGV.shift
|
65
|
+
subcommands[subcommand].parse!
|
66
|
+
|
67
|
+
case subcommand
|
68
|
+
when 'get'
|
69
|
+
username = ARGV[0]
|
70
|
+
|
71
|
+
unless username
|
72
|
+
puts 'username must be specified'
|
73
|
+
puts "see 'credsummoner --help'"
|
74
|
+
exit(1)
|
75
|
+
end
|
76
|
+
|
77
|
+
unless CredSummoner::Config.exists?
|
78
|
+
puts 'CredSummoner has not yet been configured'
|
79
|
+
puts "see 'credsummoner config --help'"
|
80
|
+
exit(1)
|
81
|
+
end
|
82
|
+
|
83
|
+
prompt = TTY::Prompt.new
|
84
|
+
user = CredSummoner::Okta::User.new(username) do
|
85
|
+
password = prompt.mask('password:')
|
86
|
+
totp_token = prompt.ask('TOTP token:')
|
87
|
+
CredSummoner::Okta::Credentials.new(password, totp_token)
|
88
|
+
end
|
89
|
+
account = if account_name
|
90
|
+
user.role_map.keys.find { |a| a.name == account_name } ||
|
91
|
+
begin
|
92
|
+
puts "account '#{account_name}' is not a valid choice"
|
93
|
+
puts 'available accounts:'
|
94
|
+
user.role_map.keys.each do |acc|
|
95
|
+
puts " - #{acc.name}"
|
96
|
+
end
|
97
|
+
exit(1)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
prompt.select('which account?') do |menu|
|
101
|
+
user.role_map.keys.each do |account|
|
102
|
+
menu.choice(account.to_s, account)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
role = if role_name
|
107
|
+
user.role_map[account].find { |r| r.name == role_name } ||
|
108
|
+
begin
|
109
|
+
puts "role '#{role_name}' is not a valid choice"
|
110
|
+
puts 'available roles:'
|
111
|
+
user.role_map[account].each do |role|
|
112
|
+
puts " - #{role.name}"
|
113
|
+
end
|
114
|
+
exit(1)
|
115
|
+
end
|
116
|
+
else
|
117
|
+
prompt.select('which role?') do |menu|
|
118
|
+
user.role_map[account].each do |role|
|
119
|
+
menu.choice(role.to_s, role)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
credentials = user.assume_role(role, duration, region)
|
124
|
+
|
125
|
+
if mode == :command
|
126
|
+
command = if ARGV.length > 1
|
127
|
+
ARGV.drop(1)
|
128
|
+
else
|
129
|
+
# Default to user's preferred shell, falling back to bash.
|
130
|
+
[ENV['SHELL']] || ['bash']
|
131
|
+
end
|
132
|
+
|
133
|
+
# Fork and use exec to spawn a child process with the AWS session
|
134
|
+
# environment variables prepared.
|
135
|
+
pid = Process.fork do
|
136
|
+
ENV['AWS_ACCESS_KEY_ID'] = credentials[:access_key_id]
|
137
|
+
ENV['AWS_SECRET_ACCESS_KEY'] = credentials[:secret_access_key]
|
138
|
+
ENV['AWS_SESSION_TOKEN'] = credentials[:session_token]
|
139
|
+
# For decorating the shell prompt.
|
140
|
+
ENV['CREDSUMMONER_AWS_ROLE'] = "#{account.name}/#{role.name}"
|
141
|
+
STDERR.puts "session expires at #{credentials[:expiration]}"
|
142
|
+
exec(*command)
|
143
|
+
end
|
144
|
+
|
145
|
+
if pid
|
146
|
+
Process.waitpid(pid)
|
147
|
+
exit($?.exitstatus) # exit with status of child process
|
148
|
+
end
|
149
|
+
elsif mode == :environment
|
150
|
+
puts "export AWS_ACCESS_KEY_ID=\"#{credentials[:access_key_id]}\""
|
151
|
+
puts "export AWS_SECRET_ACCESS_KEY=\"#{credentials[:secret_access_key]}\""
|
152
|
+
puts "export AWS_SESSION_TOKEN=\"#{credentials[:session_token]}\""
|
153
|
+
puts "export CREDSUMMONER_AWS_ROLE=\"#{account.name}/#{role.name}\""
|
154
|
+
STDERR.puts "session expires at #{credentials[:expiration]}"
|
155
|
+
end
|
156
|
+
when 'config'
|
157
|
+
key = ARGV[0]
|
158
|
+
value = ARGV[1]
|
159
|
+
config = if CredSummoner::Config.exists?
|
160
|
+
CredSummoner::Config.load
|
161
|
+
else
|
162
|
+
CredSummoner::Config.new
|
163
|
+
end
|
164
|
+
config.send("#{key}=", value)
|
165
|
+
config.save
|
166
|
+
end
|
data/lib/credsummoner.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'credsummoner/config'
|
2
|
+
require 'credsummoner/account'
|
3
|
+
require 'credsummoner/role'
|
4
|
+
require 'credsummoner/saml_assertion'
|
5
|
+
require 'credsummoner/web'
|
6
|
+
require 'credsummoner/okta/credentials'
|
7
|
+
require 'credsummoner/okta/session'
|
8
|
+
require 'credsummoner/okta/user'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module CredSummoner
|
5
|
+
class Config
|
6
|
+
attr_accessor :okta_aws_embed_link
|
7
|
+
|
8
|
+
def initialize(okta_aws_embed_link: nil)
|
9
|
+
@okta_aws_embed_link = okta_aws_embed_link
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.exists?
|
13
|
+
File.exists?(config_file)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.load
|
17
|
+
if exists?
|
18
|
+
yaml = YAML.load(File.read(config_file))
|
19
|
+
Config.new(okta_aws_embed_link: yaml['okta_aws_embed_link'])
|
20
|
+
else
|
21
|
+
raise 'no config file'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.config_dir
|
26
|
+
"#{ENV['HOME']}/.config/credsummoner"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.config_file
|
30
|
+
"#{config_dir}/config.yml"
|
31
|
+
end
|
32
|
+
|
33
|
+
def save
|
34
|
+
FileUtils.mkdir_p(Config.config_dir)
|
35
|
+
File.open(Config.config_file, 'w', 0600) do |file|
|
36
|
+
file.puts(YAML.dump(serialize))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def serialize
|
41
|
+
{
|
42
|
+
'okta_aws_embed_link' => okta_aws_embed_link
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module CredSummoner
|
4
|
+
module Okta
|
5
|
+
class Session
|
6
|
+
attr_reader :username, :get_creds
|
7
|
+
|
8
|
+
def initialize(username, get_creds)
|
9
|
+
@username = username
|
10
|
+
@get_creds = get_creds
|
11
|
+
end
|
12
|
+
|
13
|
+
def cache_dir
|
14
|
+
"#{ENV['HOME']}/.cache/credsummoner"
|
15
|
+
end
|
16
|
+
|
17
|
+
def cache_file
|
18
|
+
"#{cache_dir}/okta_session_#{username}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def lookup_cached_session
|
22
|
+
File.exists?(cache_file) && JSON.parse(File.read(cache_file))
|
23
|
+
end
|
24
|
+
|
25
|
+
def cache_session(session)
|
26
|
+
FileUtils.mkdir_p(cache_dir)
|
27
|
+
File.open(cache_file, 'w', 0600) do |file|
|
28
|
+
file.puts(session.to_json)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def clear!
|
33
|
+
File.delete(cache_file)
|
34
|
+
@data = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def aws_embed_uri
|
38
|
+
@aws_embed_uri ||= URI.parse(Config.load.okta_aws_embed_link)
|
39
|
+
end
|
40
|
+
|
41
|
+
def base_okta_url
|
42
|
+
"#{aws_embed_uri.scheme}://#{aws_embed_uri.host}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def auth_url
|
46
|
+
"#{base_okta_url}/api/v1/authn"
|
47
|
+
end
|
48
|
+
|
49
|
+
def login(creds)
|
50
|
+
response = Web.post_json(auth_url,
|
51
|
+
username: username,
|
52
|
+
password: creds.password)
|
53
|
+
|
54
|
+
if response
|
55
|
+
status = response['status']
|
56
|
+
case status
|
57
|
+
when 'SUCCESS'
|
58
|
+
response['sessionToken']
|
59
|
+
when 'MFA_REQUIRED'
|
60
|
+
# FIXME: TOTP is the only supported factor currently.
|
61
|
+
factor = response['_embedded']['factors'].find do |factor|
|
62
|
+
factor['factorType'] == 'token:software:totp'
|
63
|
+
end
|
64
|
+
mfa(factor['id'], response['stateToken'], creds)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
raise 'incorrect password'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def mfa(factor_id, state_token, creds)
|
72
|
+
response = Web.post_json("#{base_okta_url}/api/v1/authn/factors/#{factor_id}/verify",
|
73
|
+
stateToken: state_token,
|
74
|
+
passCode: creds.totp_token)
|
75
|
+
if response
|
76
|
+
response['sessionToken']
|
77
|
+
else
|
78
|
+
raise 'invalid MFA token'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def create_fresh_session
|
83
|
+
creds = get_creds.call
|
84
|
+
session_token = login(creds)
|
85
|
+
app_url_with_token = "#{aws_embed_uri.to_s}?onetimetoken=#{session_token}"
|
86
|
+
# A successful login yields a URL to redirect to and a cookie that has
|
87
|
+
# our session.
|
88
|
+
response = Web.get(app_url_with_token)
|
89
|
+
redirect_url = response['location']
|
90
|
+
# Really simple cookie parsing.
|
91
|
+
cookie = response.get_fields('set-cookie').map do |field|
|
92
|
+
field.split('; ')[0]
|
93
|
+
end.join('; ')
|
94
|
+
saml_url = Web.get(redirect_url, cookie: cookie)['location']
|
95
|
+
data = {
|
96
|
+
'saml_url' => saml_url,
|
97
|
+
'cookie' => cookie
|
98
|
+
}
|
99
|
+
cache_session(data)
|
100
|
+
data
|
101
|
+
end
|
102
|
+
|
103
|
+
def data
|
104
|
+
@data ||= lookup_cached_session || create_fresh_session
|
105
|
+
end
|
106
|
+
|
107
|
+
def saml_url
|
108
|
+
data['saml_url']
|
109
|
+
end
|
110
|
+
|
111
|
+
def cookie
|
112
|
+
data['cookie']
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module CredSummoner
|
6
|
+
module Okta
|
7
|
+
class User
|
8
|
+
attr_reader :username
|
9
|
+
|
10
|
+
def initialize(username, &blk)
|
11
|
+
@username = username
|
12
|
+
@get_creds = blk.to_proc
|
13
|
+
end
|
14
|
+
|
15
|
+
def session
|
16
|
+
@session ||= Session.new(username, @get_creds)
|
17
|
+
end
|
18
|
+
|
19
|
+
def saml_assertion
|
20
|
+
@saml_assertion ||=
|
21
|
+
begin
|
22
|
+
response = nil
|
23
|
+
while true
|
24
|
+
# Get the base64 encoded SAML assertion that we will need to
|
25
|
+
# send along to AWS.
|
26
|
+
response = Web.get(session.saml_url, cookie: session.cookie)
|
27
|
+
if response.code == '200'
|
28
|
+
break
|
29
|
+
else
|
30
|
+
# Cookie expired! Clear session and try again. The
|
31
|
+
# user will be prompted for credentials again.
|
32
|
+
session.clear!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
saml_page = response.body
|
36
|
+
SAMLAssertion.new(Nokogiri::HTML(saml_page).at_css('form input[name=SAMLResponse]')['value'])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def role_map
|
41
|
+
@role_map ||=
|
42
|
+
begin
|
43
|
+
# Two things can happen on this sign-in page:
|
44
|
+
#
|
45
|
+
# 1) The user only has access to a single role in a single
|
46
|
+
# account, in which case they are redirected straight to the
|
47
|
+
# console for that account + role
|
48
|
+
#
|
49
|
+
# 2) The user has access to more than one role in one or more
|
50
|
+
# accounts, in which case they are presented with a page that
|
51
|
+
# lists all accounts and roles for them to choose from.
|
52
|
+
#
|
53
|
+
# For case #1, the response body will be the empty string and
|
54
|
+
# the set-cookie header will contain the account + role
|
55
|
+
# information and we will parse that.
|
56
|
+
#
|
57
|
+
# For case #2, the response body will be scraped for all the
|
58
|
+
# account + role information.
|
59
|
+
#
|
60
|
+
# In both cases we return a hash table mapping accounts to
|
61
|
+
# roles.
|
62
|
+
response = Web.post_form('https://signin.aws.amazon.com/saml',
|
63
|
+
SAMLResponse: saml_assertion.response,
|
64
|
+
RelayState: '')
|
65
|
+
if response.body.empty?
|
66
|
+
cookie = response.get_fields('set-cookie').each_with_object({}) do |field, h|
|
67
|
+
key, value = field.split('; ')[0].split('=')
|
68
|
+
h[key] = value
|
69
|
+
end
|
70
|
+
user_info = JSON.parse(URI.unescape(cookie['aws-userInfo']))
|
71
|
+
split_arn = user_info['arn'].split('/')
|
72
|
+
role_name = split_arn[1]
|
73
|
+
account_id = split_arn[0].split(':')[4]
|
74
|
+
role_arn = "arn:aws:iam::#{account_id}:role/#{role_name}"
|
75
|
+
account = Account.new(user_info['alias'], account_id)
|
76
|
+
role = Role.new(
|
77
|
+
name: role_name,
|
78
|
+
arn: role_arn,
|
79
|
+
principal_arn: saml_assertion.principal_arn_map[role_arn]
|
80
|
+
)
|
81
|
+
{ account => [role] }
|
82
|
+
else
|
83
|
+
# Time for a little web scraping. Create an account -> roles mapping
|
84
|
+
# so that we can present the user with a list of roles to choose from.
|
85
|
+
role_page = response.body
|
86
|
+
html = Nokogiri::HTML(role_page)
|
87
|
+
accounts = html.css('div[class=saml-account-name]').map do |node|
|
88
|
+
# example account text we are parsing:
|
89
|
+
# Account: maestro-staging (774082247212)
|
90
|
+
parts = node.text.split(' ')
|
91
|
+
name = parts[1]
|
92
|
+
id = parts[2][1..-2] # account name is in parens, trim those off
|
93
|
+
Account.new(name, id)
|
94
|
+
end
|
95
|
+
roles = html.css('div[class=saml-account] div[class=saml-account]').map do |node|
|
96
|
+
node.css('input[name=roleIndex]').map do |field|
|
97
|
+
id = field['id']
|
98
|
+
arn = field['value']
|
99
|
+
# Extract the human readable role name.
|
100
|
+
name = node.css("label[for='#{id}']").text
|
101
|
+
Role.new(name: name, arn: arn,
|
102
|
+
principal_arn: saml_assertion.principal_arn_map[arn])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
accounts.zip(roles).to_h
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def assume_role(role, duration, region)
|
111
|
+
role.assume(saml_assertion, duration, region)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'aws-sdk-core'
|
2
|
+
|
3
|
+
class Role
|
4
|
+
attr_reader :name, :arn, :principal_arn
|
5
|
+
|
6
|
+
def initialize(name:, arn:, principal_arn:)
|
7
|
+
@name = name
|
8
|
+
@arn = arn
|
9
|
+
@principal_arn = principal_arn
|
10
|
+
end
|
11
|
+
|
12
|
+
def assume(saml, duration, region)
|
13
|
+
sts = Aws::STS::Client.new(region: region)
|
14
|
+
sts.assume_role_with_saml(
|
15
|
+
principal_arn: principal_arn,
|
16
|
+
role_arn: arn,
|
17
|
+
saml_assertion: saml.response,
|
18
|
+
duration_seconds: duration
|
19
|
+
).credentials
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
name
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module CredSummoner
|
5
|
+
class SAMLAssertion
|
6
|
+
attr_reader :response
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
@response = response
|
10
|
+
end
|
11
|
+
|
12
|
+
def xml_tree
|
13
|
+
@xml_tree ||= Nokogiri::XML(Base64.decode64(response))
|
14
|
+
end
|
15
|
+
|
16
|
+
# Role->Principal mapping
|
17
|
+
def principal_arn_map
|
18
|
+
@principal_arn_map ||=
|
19
|
+
begin
|
20
|
+
# The SAML document has the principal ARNs and role ARNs in
|
21
|
+
# "principal,role" pairs. So, we generate a mapping from role
|
22
|
+
# to principal for lookup later when we talk to AWS STS to
|
23
|
+
# create a session.
|
24
|
+
saml_xpath = "//saml2:Attribute[@Name='https://aws.amazon.com/SAML/Attributes/Role']/saml2:AttributeValue"
|
25
|
+
saml_namespace = 'urn:oasis:names:tc:SAML:2.0:assertion'
|
26
|
+
xml_tree.xpath(saml_xpath, saml2: saml_namespace).map do |node|
|
27
|
+
node.text.split(',').reverse
|
28
|
+
end.to_h
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module CredSummoner
|
5
|
+
class Web
|
6
|
+
def self.get(url, cookie: nil)
|
7
|
+
uri = URI.parse(url)
|
8
|
+
http = Net::HTTP::new(uri.host, uri.port)
|
9
|
+
http.use_ssl = true
|
10
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
11
|
+
request['Cookie'] = cookie if cookie
|
12
|
+
http.request(request)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.post_form(url, form_data)
|
16
|
+
uri = URI.parse(url)
|
17
|
+
http = Net::HTTP::new(uri.host, uri.port)
|
18
|
+
http.use_ssl = true
|
19
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
20
|
+
request.set_form_data(form_data)
|
21
|
+
http.request(request)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.post_json(url, args)
|
25
|
+
uri = URI.parse(url)
|
26
|
+
http = Net::HTTP::new(uri.host, uri.port)
|
27
|
+
http.use_ssl = true
|
28
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
29
|
+
request.body = args.to_json
|
30
|
+
request.content_type = 'application/json'
|
31
|
+
response = http.request(request)
|
32
|
+
if response.code == '200'
|
33
|
+
JSON.parse(response.body)
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: credsummoner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Thompson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-07-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: tty-prompt
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.19'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.19'
|
55
|
+
description: Retrieve temporary AWS credentials via an identity provider.
|
56
|
+
email: dthompson@vistahigherlearning.com
|
57
|
+
executables:
|
58
|
+
- credsummoner
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- bin/credsummoner
|
63
|
+
- lib/credsummoner.rb
|
64
|
+
- lib/credsummoner/account.rb
|
65
|
+
- lib/credsummoner/config.rb
|
66
|
+
- lib/credsummoner/okta/credentials.rb
|
67
|
+
- lib/credsummoner/okta/session.rb
|
68
|
+
- lib/credsummoner/okta/user.rb
|
69
|
+
- lib/credsummoner/role.rb
|
70
|
+
- lib/credsummoner/saml_assertion.rb
|
71
|
+
- lib/credsummoner/web.rb
|
72
|
+
homepage: https://github.com/vhl/credsummoner
|
73
|
+
licenses:
|
74
|
+
- GPL-3.0+
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubygems_version: 3.0.3
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Retrieve temporary AWS credentials via an identity provider
|
95
|
+
test_files: []
|