MuranoCLI 3.1.0 → 3.2.0.beta.1
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/.rubocop.yml +2 -0
- data/.trustme.sh +6 -2
- data/lib/MrMurano/Account.rb +8 -262
- data/lib/MrMurano/AccountBase.rb +214 -0
- data/lib/MrMurano/Business.rb +3 -19
- data/lib/MrMurano/Config-Migrate.rb +0 -4
- data/lib/MrMurano/Config.rb +159 -70
- data/lib/MrMurano/Content.rb +3 -2
- data/lib/MrMurano/Exchange.rb +3 -1
- data/lib/MrMurano/Gateway.rb +3 -2
- data/lib/MrMurano/HttpAuthed.rb +287 -0
- data/lib/MrMurano/Passwords.rb +82 -35
- data/lib/MrMurano/ReCommander.rb +2 -0
- data/lib/MrMurano/Solution.rb +3 -2
- data/lib/MrMurano/SyncRoot.rb +2 -0
- data/lib/MrMurano/Webservice.rb +3 -2
- data/lib/MrMurano/commands/business.rb +2 -2
- data/lib/MrMurano/commands/globals.rb +41 -0
- data/lib/MrMurano/commands/init.rb +1 -1
- data/lib/MrMurano/commands/login.rb +30 -6
- data/lib/MrMurano/commands/logs.rb +1 -6
- data/lib/MrMurano/commands/password.rb +15 -11
- data/lib/MrMurano/commands/show.rb +1 -1
- data/lib/MrMurano/commands/token.rb +209 -0
- data/lib/MrMurano/commands.rb +1 -0
- data/lib/MrMurano/http.rb +13 -24
- data/lib/MrMurano/version.rb +1 -1
- data/lib/MrMurano.rb +1 -0
- data/spec/Account_spec.rb +8 -8
- data/spec/Http_spec.rb +0 -1
- data/spec/cmd_token_spec.rb +56 -0
- data/spec/fixtures/websocket/README.rst +2 -2
- metadata +8 -4
data/lib/MrMurano/Passwords.rb
CHANGED
@@ -13,17 +13,17 @@ require 'yaml'
|
|
13
13
|
require 'MrMurano/http'
|
14
14
|
require 'MrMurano/verbosing'
|
15
15
|
require 'MrMurano/Config'
|
16
|
-
require 'MrMurano/Solution'
|
17
16
|
|
18
17
|
module MrMurano
|
19
|
-
# The
|
20
|
-
|
18
|
+
# The LocalStorage class manages an end user's Murano user name and secrets.
|
19
|
+
module LocalStorage
|
21
20
|
include Verbose
|
22
21
|
|
23
|
-
def initialize(path=nil)
|
24
|
-
path = $cfg.file_at(
|
22
|
+
def initialize(path=nil, val_type=nil)
|
23
|
+
path = $cfg.file_at(val_type, :user) if path.nil?
|
25
24
|
path = Pathname.new(path) unless path.is_a?(Pathname)
|
26
25
|
@path = path
|
26
|
+
@val_type = val_type
|
27
27
|
@data = nil
|
28
28
|
end
|
29
29
|
|
@@ -32,7 +32,13 @@ module MrMurano
|
|
32
32
|
@path.chmod(0o600)
|
33
33
|
@path.open('rb') do |io|
|
34
34
|
@data = YAML.load(io)
|
35
|
+
# The @data is, e.g., false, if the file exists but is empty.
|
36
|
+
@data = {} unless @data.is_a?(Hash)
|
35
37
|
end
|
38
|
+
rescue StandardError => err
|
39
|
+
warning %(Could not load #{fancy_ticks(@val_type)} file at: #{@path})
|
40
|
+
error err.message
|
41
|
+
exit 1
|
36
42
|
end
|
37
43
|
|
38
44
|
def save
|
@@ -47,59 +53,100 @@ module MrMurano
|
|
47
53
|
@path.chmod(0o600)
|
48
54
|
end
|
49
55
|
|
50
|
-
def
|
56
|
+
def get(key1, key2)
|
57
|
+
lookup(key1, key2)
|
58
|
+
end
|
59
|
+
|
60
|
+
def set(key1, key2, val)
|
51
61
|
unless @data.is_a?(Hash)
|
52
|
-
@data = {
|
62
|
+
@data = { key1 => { key2 => val } }
|
53
63
|
return
|
54
64
|
end
|
55
|
-
hd = @data[
|
65
|
+
hd = @data[key1]
|
56
66
|
if hd.nil? || !hd.is_a?(Hash)
|
57
|
-
@data[
|
67
|
+
@data[key1] = { key2 => val }
|
58
68
|
return
|
59
69
|
end
|
60
|
-
@data[
|
70
|
+
@data[key1][key2] = val
|
61
71
|
end
|
62
72
|
|
63
|
-
def
|
64
|
-
return ENV['MURANO_PASSWORD'] unless ENV['MURANO_PASSWORD'].to_s.empty?
|
65
|
-
unless ENV['MR_PASSWORD'].nil?
|
66
|
-
warning %(
|
67
|
-
Using deprecated environment variable, "MR_PASSWORD". Please rename to "MURANO_PASSWORD"
|
68
|
-
).strip
|
69
|
-
return ENV['MR_PASSWORD']
|
70
|
-
end
|
71
|
-
lookup(host, user)
|
72
|
-
end
|
73
|
-
|
74
|
-
def lookup(host, user)
|
73
|
+
def lookup(key1, key2)
|
75
74
|
return nil unless @data.is_a?(Hash)
|
76
|
-
return nil unless @data.key?(
|
77
|
-
return nil unless @data[
|
78
|
-
return nil unless @data[
|
79
|
-
@data[
|
75
|
+
return nil unless @data.key?(key1)
|
76
|
+
return nil unless @data[key1].is_a?(Hash)
|
77
|
+
return nil unless @data[key1].key?(key2)
|
78
|
+
@data[key1][key2]
|
80
79
|
end
|
81
80
|
|
82
|
-
## Remove the password for a user.
|
83
|
-
def remove(
|
81
|
+
## Remove the value (password or token) for a doubly-keyed item (host => user).
|
82
|
+
def remove(key1, key2)
|
84
83
|
return unless @data.is_a?(Hash)
|
85
|
-
hd = @data[
|
84
|
+
hd = @data[key1]
|
86
85
|
return unless !hd.nil? && hd.is_a?(Hash)
|
87
86
|
if $cfg['tool.dry']
|
88
87
|
MrMurano::Verbose.whirly_interject do
|
89
|
-
say(%(--dry: Not removing
|
88
|
+
say(%(--dry: Not removing #{@val_type} for #{fancy_ticks("#{key2}@#{host}")}))
|
90
89
|
end
|
91
90
|
return
|
92
91
|
end
|
93
|
-
@data[
|
92
|
+
@data[key1].delete(key2) if hd.key?(key2)
|
93
|
+
@data.delete(key1) if @data[key1].empty?
|
94
94
|
end
|
95
95
|
|
96
|
-
## Get all
|
96
|
+
## Get all first- and second-level keys. Does not return values.
|
97
|
+
## E.g., get all hosts and usernames, but not passwords (or tokens).
|
97
98
|
def list
|
98
99
|
ret = {}
|
99
|
-
|
100
|
+
unless @data.nil?
|
101
|
+
@data.each_pair do |key1, key2_lkup|
|
102
|
+
ret[key1] = key2_lkup.keys
|
103
|
+
end
|
104
|
+
end
|
100
105
|
ret
|
101
|
-
|
102
|
-
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module MrMurano
|
111
|
+
# The Passwords class manages an end user's Murano user name and password.
|
112
|
+
class Passwords
|
113
|
+
include LocalStorage
|
114
|
+
|
115
|
+
FILENAME = 'passwords'
|
116
|
+
|
117
|
+
def initialize(path=nil, val_type=nil)
|
118
|
+
val_type = FILENAME if path.nil?
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
def get(host, user)
|
123
|
+
return ENV['MURANO_PASSWORD'] unless ENV['MURANO_PASSWORD'].to_s.empty?
|
124
|
+
unless ENV['MR_PASSWORD'].nil?
|
125
|
+
warning %(
|
126
|
+
Using deprecated environment variable, "MR_PASSWORD". Please rename to "MURANO_PASSWORD"
|
127
|
+
).strip
|
128
|
+
return ENV['MR_PASSWORD']
|
129
|
+
end
|
130
|
+
super
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
module MrMurano
|
136
|
+
# The Tokens class manages an end user's Murano user name and tokens.
|
137
|
+
class Tokens
|
138
|
+
include LocalStorage
|
139
|
+
|
140
|
+
FILENAME = 'tokens'
|
141
|
+
|
142
|
+
def initialize(path=nil, val_type=nil)
|
143
|
+
val_type = FILENAME if path.nil?
|
144
|
+
super
|
145
|
+
end
|
146
|
+
|
147
|
+
def get(host, user)
|
148
|
+
return ENV['MURANO_TOKEN'] unless ENV['MURANO_TOKEN'].to_s.empty?
|
149
|
+
super
|
103
150
|
end
|
104
151
|
end
|
105
152
|
end
|
data/lib/MrMurano/ReCommander.rb
CHANGED
@@ -78,6 +78,8 @@ module Commander
|
|
78
78
|
alias old_run_active_command run_active_command
|
79
79
|
def run_active_command
|
80
80
|
exit @command_exit if defined?(@command_exit) && @command_exit
|
81
|
+
$cfg.must_be_valid_values!
|
82
|
+
|
81
83
|
section = active_command.name
|
82
84
|
hooked = MrMurano::Hooked.new(section)
|
83
85
|
hooked.check_run_pre_hook
|
data/lib/MrMurano/Solution.rb
CHANGED
@@ -7,19 +7,20 @@
|
|
7
7
|
|
8
8
|
require 'rainbow'
|
9
9
|
require 'uri'
|
10
|
-
require 'MrMurano/http'
|
11
10
|
require 'MrMurano/verbosing'
|
11
|
+
require 'MrMurano/AccountBase'
|
12
12
|
require 'MrMurano/Config'
|
13
13
|
require 'MrMurano/SolutionId'
|
14
14
|
require 'MrMurano/SyncUpDown'
|
15
15
|
|
16
16
|
module MrMurano
|
17
17
|
class SolutionBase
|
18
|
-
include
|
18
|
+
include AccountBase
|
19
19
|
include Verbose
|
20
20
|
include SolutionId
|
21
21
|
|
22
22
|
def initialize(from=nil)
|
23
|
+
super()
|
23
24
|
@uriparts_apidex = 1
|
24
25
|
# Introspection. Feels hacky.
|
25
26
|
if from.is_a? MrMurano::Solution
|
data/lib/MrMurano/SyncRoot.rb
CHANGED
data/lib/MrMurano/Webservice.rb
CHANGED
@@ -7,8 +7,8 @@
|
|
7
7
|
|
8
8
|
require 'uri'
|
9
9
|
require 'MrMurano/Config'
|
10
|
-
require 'MrMurano/http'
|
11
10
|
require 'MrMurano/verbosing'
|
11
|
+
require 'MrMurano/AccountBase'
|
12
12
|
require 'MrMurano/SolutionId'
|
13
13
|
require 'MrMurano/SyncUpDown'
|
14
14
|
|
@@ -16,11 +16,12 @@ module MrMurano
|
|
16
16
|
## The details of talking to the Webservice service.
|
17
17
|
module Webservice
|
18
18
|
class WebserviceBase
|
19
|
-
include
|
19
|
+
include AccountBase
|
20
20
|
include Verbose
|
21
21
|
include SolutionId
|
22
22
|
|
23
23
|
def initialize
|
24
|
+
super
|
24
25
|
@solntype = 'application.id'
|
25
26
|
@uriparts_apidex = 1
|
26
27
|
init_api_id!
|
@@ -65,7 +65,7 @@ end
|
|
65
65
|
|
66
66
|
def cmd_verify_args_and_id_or_name!(args, options)
|
67
67
|
return unless args.none? && (options.id || options.name)
|
68
|
-
MrMurano::Verbose.
|
68
|
+
MrMurano::Verbose.error(
|
69
69
|
'The --id and --name options only apply when specifying a business name or ID.'
|
70
70
|
)
|
71
71
|
exit 1
|
@@ -229,7 +229,7 @@ end
|
|
229
229
|
|
230
230
|
def cmd_business_find_and_output(args, options)
|
231
231
|
cmd_verify_args_and_id_or_name!(args, options)
|
232
|
-
acc = MrMurano::Account.
|
232
|
+
acc = MrMurano::Account.new
|
233
233
|
bizz = cmd_business_find_businesses(acc, args, options)
|
234
234
|
if bizz.empty? && !options.idonly
|
235
235
|
MrMurano::Verbose.error(MSG_BUSINESSES_NONE_FOUND)
|
@@ -5,6 +5,7 @@
|
|
5
5
|
# vim:tw=0:ts=2:sw=2:et:ai
|
6
6
|
# Unauthorized copying of this file is strictly prohibited.
|
7
7
|
|
8
|
+
require 'MrMurano/verbosing'
|
8
9
|
require 'MrMurano/Config'
|
9
10
|
|
10
11
|
global_option('--[no-]color', %(Disable fancy output)) do |value|
|
@@ -93,3 +94,43 @@ global_option('--sid VALUE', %(Override application or product ID)) do |value|
|
|
93
94
|
$cfg['product.id'] = value
|
94
95
|
end
|
95
96
|
|
97
|
+
global_option(
|
98
|
+
'--[no-]token', %(Use token authentication (using temporary session token)),
|
99
|
+
) do |value|
|
100
|
+
$cfg['auth.scheme-token'] = value
|
101
|
+
end
|
102
|
+
|
103
|
+
global_option(
|
104
|
+
'--[no-]basic', %(Use basic authentication (using username and password)),
|
105
|
+
) do |value|
|
106
|
+
$cfg['auth.scheme-basic'] = value
|
107
|
+
end
|
108
|
+
|
109
|
+
global_option(
|
110
|
+
'--[no-]login', %(Cache access token for repeated use (default: true)),
|
111
|
+
) do |value|
|
112
|
+
$cfg['auth.persist-token'] = value
|
113
|
+
end
|
114
|
+
|
115
|
+
global_option(
|
116
|
+
'--[no-]cache', %(Cache password locally for repeated use)
|
117
|
+
) do |value|
|
118
|
+
$cfg['auth.persist-basic'] = value
|
119
|
+
end
|
120
|
+
|
121
|
+
global_option(
|
122
|
+
'--ttl TTL', %(Set token time-to-live in seconds (default: 172800)),
|
123
|
+
) do |value|
|
124
|
+
# (lb): What's the point of being able to tell rb-commander the arg type, e.g.,
|
125
|
+
# global_option('--ttl TTL', Integer, %(Foo))
|
126
|
+
# if its response to bad input is to raise an exception and dump a stack trace?
|
127
|
+
# Because to produce a meaningful message for the user, we have to process
|
128
|
+
# it ourselves anyway, like so:
|
129
|
+
begin
|
130
|
+
$cfg['auth.ttl'] = Integer(value)
|
131
|
+
rescue ArgumentError => _err
|
132
|
+
MrMurano::Verbose.error(%(Token TTL not an integer: #{value}))
|
133
|
+
exit 1
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
@@ -134,7 +134,7 @@ command :init do |c|
|
|
134
134
|
c.verify_arg_count!(args, 1)
|
135
135
|
options.default(refresh: false, purge: false, sync: true, mkdirs: true)
|
136
136
|
|
137
|
-
acc = MrMurano::Account.
|
137
|
+
acc = MrMurano::Account.new
|
138
138
|
validate_dir!(acc, args, options)
|
139
139
|
|
140
140
|
puts('')
|
@@ -25,7 +25,9 @@ If you are having trouble logging in, try deleting the saved password first:
|
|
25
25
|
|
26
26
|
c.action do |args, options|
|
27
27
|
c.verify_arg_count!(args)
|
28
|
-
|
28
|
+
# By default, unless user specified --no-token or --basic,
|
29
|
+
# Account will save the token to the tokens file.
|
30
|
+
tok = MrMurano::Account.new.token
|
29
31
|
say tok if options.show_token
|
30
32
|
end
|
31
33
|
end
|
@@ -41,17 +43,39 @@ it will remove that user's password from the password file.
|
|
41
43
|
|
42
44
|
Essentially, this command is the same as:
|
43
45
|
|
44
|
-
murano
|
45
|
-
murano password delete
|
46
|
-
murano
|
46
|
+
cur_user="$(murano config user.name)"
|
47
|
+
murano password delete "${cur_user}"
|
48
|
+
murano token delete
|
49
|
+
if [[ "${cur_user}" == "$(murano config user.name --user)" ]]; then
|
50
|
+
murano config --unset --user user.name
|
51
|
+
murano config --unset --user business.id
|
52
|
+
murano config --unset --user business.name
|
53
|
+
fi
|
47
54
|
).strip
|
48
55
|
c.project_not_required = true
|
49
56
|
|
50
|
-
c.option '--
|
57
|
+
c.option '--keep-user', 'Only delete the token and clear the password'
|
51
58
|
|
52
59
|
c.action do |args, options|
|
53
60
|
c.verify_arg_count!(args)
|
54
|
-
|
61
|
+
|
62
|
+
MrMurano::Account.new.logout(keep_user: options.keep_user)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
command 'whoami' do |c|
|
67
|
+
c.syntax = %(murano whoami)
|
68
|
+
c.summary = %(Who are you?)
|
69
|
+
c.description = %(
|
70
|
+
Show user.name of current working directory
|
71
|
+
|
72
|
+
Essentially, this command is the same as:
|
73
|
+
|
74
|
+
murano config user.name
|
75
|
+
).strip
|
76
|
+
c.project_not_required = true
|
77
|
+
c.action do |_args, _options|
|
78
|
+
puts $cfg['user.name']
|
55
79
|
end
|
56
80
|
end
|
57
81
|
|
@@ -138,11 +138,6 @@ Used to group logs together for one endpoint request.
|
|
138
138
|
).strip
|
139
139
|
end
|
140
140
|
|
141
|
-
# NOTE (landonb): The Service & Script Debug Log RFC mentions 'subject',
|
142
|
-
# but I've never seen it in any debug log reply.
|
143
|
-
# The Subject line can be used for a short summary.
|
144
|
-
# It should be shorter than 80 characters.
|
145
|
-
|
146
141
|
def cmd_add_examples(cmd)
|
147
142
|
cmd.example %(
|
148
143
|
View the last 100 product log entries
|
@@ -173,7 +168,7 @@ Used to group logs together for one endpoint request.
|
|
173
168
|
).strip, 'murano logs --follow -T :script'
|
174
169
|
|
175
170
|
cmd.example %(
|
176
|
-
Show last 100 logs with any severity level
|
171
|
+
Show last 100 logs with any severity level except DEBUG
|
177
172
|
).strip, 'murano logs --severity 0-6'
|
178
173
|
|
179
174
|
cmd.example %(
|
@@ -5,13 +5,14 @@
|
|
5
5
|
# vim:tw=0:ts=2:sw=2:et:ai
|
6
6
|
# Unauthorized copying of this file is strictly prohibited.
|
7
7
|
|
8
|
+
require 'MrMurano/Passwords'
|
8
9
|
require 'MrMurano/ReCommander'
|
9
10
|
|
10
11
|
command :password do |c|
|
11
12
|
c.syntax = %(murano password)
|
12
13
|
c.summary = %(About password commands)
|
13
14
|
c.description = %(
|
14
|
-
Commands for working with usernames and passwords.
|
15
|
+
Commands for working with usernames and passwords.
|
15
16
|
).strip
|
16
17
|
c.project_not_required = true
|
17
18
|
c.subcmdgrouphelp = true
|
@@ -25,9 +26,9 @@ alias_command 'password current', :config, 'user.name'
|
|
25
26
|
|
26
27
|
command 'password list' do |c|
|
27
28
|
c.syntax = %(murano password list)
|
28
|
-
c.summary = %(List the usernames with saved passwords)
|
29
|
+
c.summary = %(List the usernames and hosts with saved passwords)
|
29
30
|
c.description = %(
|
30
|
-
List the usernames and hosts that have
|
31
|
+
List the usernames and hosts that have a password saved locally.
|
31
32
|
).strip
|
32
33
|
c.project_not_required = true
|
33
34
|
|
@@ -43,6 +44,7 @@ List the usernames and hosts that have been saved.
|
|
43
44
|
dd.each_pair do |key, value|
|
44
45
|
value.each { |v| rows << [key, v] }
|
45
46
|
end
|
47
|
+
rows.sort_by! { |kv| kv[0] }
|
46
48
|
psd.tabularize(
|
47
49
|
{ rows: rows, headers: %i[Host Username] },
|
48
50
|
ios
|
@@ -53,13 +55,13 @@ end
|
|
53
55
|
alias_command 'passwords list', 'password list'
|
54
56
|
|
55
57
|
command 'password set' do |c|
|
56
|
-
c.syntax = %(murano password set <username> [<host>])
|
57
|
-
c.summary = %(Set password for username)
|
58
|
+
c.syntax = %(murano password set [<username>] [<host>])
|
59
|
+
c.summary = %(Set password for username and host)
|
58
60
|
c.description = %(
|
59
|
-
Set password for username.
|
61
|
+
Set password for username and host.
|
60
62
|
).strip
|
61
63
|
c.option '--password PASSWORD', String, %(The password to use)
|
62
|
-
c.option '--
|
64
|
+
c.option '--from-env', %(Use password in MURANO_PASSWORD)
|
63
65
|
c.project_not_required = true
|
64
66
|
|
65
67
|
c.action do |args, options|
|
@@ -76,7 +78,7 @@ Set password for username.
|
|
76
78
|
elsif options.from_env
|
77
79
|
pws = ENV['MURANO_PASSWORD']
|
78
80
|
else
|
79
|
-
pws = ask('Password:
|
81
|
+
pws = ask('Password: ') { |q| q.echo = '*' }
|
80
82
|
end
|
81
83
|
psd.set(host, username, pws)
|
82
84
|
psd.save
|
@@ -85,13 +87,15 @@ end
|
|
85
87
|
|
86
88
|
command 'password delete' do |c|
|
87
89
|
c.syntax = %(murano password delete <username> [<host>])
|
88
|
-
c.summary = %(Delete password for username)
|
90
|
+
c.summary = %(Delete password for username and host)
|
89
91
|
c.description = %(
|
90
|
-
Delete password for username.
|
92
|
+
Delete password for username and host.
|
91
93
|
).strip
|
92
94
|
c.project_not_required = true
|
93
95
|
|
94
|
-
c.option(
|
96
|
+
c.option(
|
97
|
+
'-y', '--[no-]yes', %(Answer "yes" to all prompts and run non-interactively),
|
98
|
+
)
|
95
99
|
|
96
100
|
c.action do |args, options|
|
97
101
|
c.verify_arg_count!(args, 2, ['Missing username'])
|
@@ -0,0 +1,209 @@
|
|
1
|
+
# Copyright © 2016-2017 Exosite LLC. All Rights Reserved
|
2
|
+
# License: PROPRIETARY. See LICENSE.txt.
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
# vim:tw=0:ts=2:sw=2:et:ai
|
6
|
+
# Unauthorized copying of this file is strictly prohibited.
|
7
|
+
|
8
|
+
require 'MrMurano/verbosing'
|
9
|
+
require 'MrMurano/Account'
|
10
|
+
require 'MrMurano/Config'
|
11
|
+
require 'MrMurano/HttpAuthed'
|
12
|
+
require 'MrMurano/Passwords'
|
13
|
+
require 'MrMurano/ReCommander'
|
14
|
+
|
15
|
+
def args_parse_and_cfg_set(args, cfg_key)
|
16
|
+
value = args.shift
|
17
|
+
value = $cfg[cfg_key] if value.to_s.empty?
|
18
|
+
$cfg[cfg_key] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def args_parse_user_host_pair(args)
|
22
|
+
username = args_parse_and_cfg_set(args, 'user.name')
|
23
|
+
host = args_parse_and_cfg_set(args, 'net.host')
|
24
|
+
[username, host]
|
25
|
+
end
|
26
|
+
|
27
|
+
def args_opts_parse_token!(args, options)
|
28
|
+
n_specified = 0
|
29
|
+
token = args.shift
|
30
|
+
n_specified += 1 unless token.nil?
|
31
|
+
if options.value
|
32
|
+
token = options.value
|
33
|
+
n_specified += 1
|
34
|
+
end
|
35
|
+
if options.from_env
|
36
|
+
token = ENV['MURANO_TOKEN']
|
37
|
+
if token.to_s.empty?
|
38
|
+
MrMurano::Verbose.error(%(--from-env specified but MURANO_TOKEN is empty))
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
n_specified += 1
|
42
|
+
end
|
43
|
+
if n_specified > 1
|
44
|
+
MrMurano::Verbose.error(%(Please specify only one token, --token, or --from-env))
|
45
|
+
exit 1
|
46
|
+
elsif n_specified == 0
|
47
|
+
token = ask('Token: ')
|
48
|
+
end
|
49
|
+
if token.to_s.empty?
|
50
|
+
MrMurano::Verbose.error(%(Please specify a token!))
|
51
|
+
unless ENV['MURANO_TOKEN'].to_s.empty?
|
52
|
+
MrMurano::Verbose.warning(
|
53
|
+
%(If you want to use MURANO_TOKEN, specify --from-env)
|
54
|
+
)
|
55
|
+
end
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
token
|
59
|
+
end
|
60
|
+
|
61
|
+
command :token do |c|
|
62
|
+
c.syntax = %(murano token)
|
63
|
+
c.summary = %(About token commands)
|
64
|
+
c.description = %(
|
65
|
+
Commands for working with uesrnames and authentication tokens.
|
66
|
+
).strip
|
67
|
+
c.project_not_required = true
|
68
|
+
c.subcmdgrouphelp = true
|
69
|
+
|
70
|
+
c.action do |_args, _options|
|
71
|
+
::Commander::UI.enable_paging unless $cfg['tool.no-page']
|
72
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# DRY: This block is a lot like the 'password list' command.
|
77
|
+
command 'token list' do |c|
|
78
|
+
c.syntax = %(murano token list)
|
79
|
+
c.summary = %(List the usernames and hosts with saved tokens)
|
80
|
+
c.description = %(
|
81
|
+
List the usernames and hosts that have a token saved locally.
|
82
|
+
).strip
|
83
|
+
c.project_not_required = true
|
84
|
+
|
85
|
+
c.action do |args, _options|
|
86
|
+
c.verify_arg_count!(args)
|
87
|
+
|
88
|
+
token_file = MrMurano::Tokens.new
|
89
|
+
token_file.load
|
90
|
+
|
91
|
+
ret = token_file.list
|
92
|
+
token_file.outf(ret) do |dd, ios|
|
93
|
+
rows = []
|
94
|
+
dd.each_pair do |key, value|
|
95
|
+
value.each do |v|
|
96
|
+
rows << [key, v]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
rows.sort_by! { |kv| kv[0] }
|
100
|
+
token_file.tabularize(
|
101
|
+
{ rows: rows, headers: %i[Host Username] },
|
102
|
+
ios
|
103
|
+
)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
alias_command 'tokens list', 'token list'
|
108
|
+
|
109
|
+
command 'token get' do |c|
|
110
|
+
c.syntax = %(murano token get [<username>] [<host>])
|
111
|
+
c.summary = %(Get token from host for username)
|
112
|
+
ttls = MrMurano::Config::CFG_AUTH_DEFAULT_TTL
|
113
|
+
c.description = %(
|
114
|
+
Get token from host for username.
|
115
|
+
|
116
|
+
By default, murano will use token authentication,
|
117
|
+
and murano will cache the token for later use.
|
118
|
+
|
119
|
+
You can instead use password authentication on an
|
120
|
+
individual command basis using --basic, or permanently
|
121
|
+
by setting the config option auth.scheme-basic=true.
|
122
|
+
|
123
|
+
You can also choose to cache your password locally using
|
124
|
+
the --cache runtime option, or the auth.persist-basic
|
125
|
+
config value. If you would not like to cache the token,
|
126
|
+
specify --no-login, or set auth.persist-token=false.
|
127
|
+
|
128
|
+
When using cache token authentication, the TTL defaults
|
129
|
+
to 172800 seconds, or 2 days. If you choose not to persist
|
130
|
+
the token, the default is #{ttls} seconds. You can override the
|
131
|
+
TTL with the --ttl option. Note that the --ttl option only
|
132
|
+
pertains to token authentication, and does not affect basic
|
133
|
+
authentication.
|
134
|
+
).strip.gsub(/^ {4}/, '')
|
135
|
+
c.option(
|
136
|
+
'--[no-]login', %(Cache access token for repeated use (default: true)),
|
137
|
+
)
|
138
|
+
c.option(
|
139
|
+
'--ttl TTL', Integer, %(Set token time-to-live in seconds (default: 172800)),
|
140
|
+
)
|
141
|
+
c.project_not_required = true
|
142
|
+
c.prompt_if_logged_off = true
|
143
|
+
|
144
|
+
c.action do |args, options|
|
145
|
+
c.verify_arg_count!(args, 2)
|
146
|
+
_username, _host = args_parse_user_host_pair(args)
|
147
|
+
$cfg['auth.ttl'] = options.ttl unless options.ttl.to_s.empty?
|
148
|
+
$cfg['auth.scheme-token'] = true
|
149
|
+
$cfg['auth.scheme-basic'] = false
|
150
|
+
# Make sure persist-token is not nil, else it'll be defaulted to true.
|
151
|
+
if options.login.nil?
|
152
|
+
$cfg['auth.persist-token'] = false
|
153
|
+
else
|
154
|
+
$cfg['auth.persist-token'] = options.login
|
155
|
+
end
|
156
|
+
$cfg['auth.persist-basic'] = false
|
157
|
+
# Create the Account *after* setting up $cfg.
|
158
|
+
acc = MrMurano::Account.new
|
159
|
+
# The HttpAuthed class will token_save or not, depending on persist-token.
|
160
|
+
token = acc.token
|
161
|
+
puts token
|
162
|
+
end
|
163
|
+
end
|
164
|
+
alias_command 'token fetch', 'token get'
|
165
|
+
|
166
|
+
command 'token set' do |c|
|
167
|
+
c.syntax = %(murano token set [<username>] [<host>] [<token>])
|
168
|
+
c.summary = %(Set token for username and host)
|
169
|
+
c.description = %(
|
170
|
+
Set token for username and host.
|
171
|
+
).strip
|
172
|
+
# We cannot use --token, because it's already a global_option.
|
173
|
+
c.option '--value TOKEN', String, %(The token to use)
|
174
|
+
c.option '--from-env', %(Use token in MURANO_TOKEN)
|
175
|
+
c.project_not_required = true
|
176
|
+
|
177
|
+
c.action do |args, options|
|
178
|
+
c.verify_arg_count!(args, 3)
|
179
|
+
username, host = args_parse_user_host_pair(args)
|
180
|
+
token = args_opts_parse_token!(args, options)
|
181
|
+
token_file = MrMurano::Tokens.new
|
182
|
+
token_file.load
|
183
|
+
token_file.set(host, username, token)
|
184
|
+
token_file.save
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
command 'token delete' do |c|
|
189
|
+
c.syntax = %(murano token delete [<username>] [<host>])
|
190
|
+
c.summary = %(Invalidate and delete token for username and host)
|
191
|
+
c.description = %(
|
192
|
+
Invalidate and delete token for username and host.
|
193
|
+
).strip
|
194
|
+
c.option '--no-invalidate', %(Do not also invalidate token on server)
|
195
|
+
c.project_not_required = true
|
196
|
+
|
197
|
+
c.action do |args, options|
|
198
|
+
c.verify_arg_count!(args, 2)
|
199
|
+
# We confirm deleting passwords, but not throwaway tokens.
|
200
|
+
username, host = args_parse_user_host_pair(args)
|
201
|
+
# We can create an authless instance, which won't ask user for
|
202
|
+
# login creds, because the delete command does not need creds.
|
203
|
+
acc = MrMurano::Account.new(authless: true)
|
204
|
+
token = MrMurano::HttpAuthed.instance.token_from_file(host, username)
|
205
|
+
acc.invalidate_token(token) unless options.no_invalidate
|
206
|
+
MrMurano::HttpAuthed.instance.token_remove(host, username)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|