MuranoCLI 3.1.0 → 3.2.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|