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.
@@ -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 Passwords class manages an end user's Murano user name and password.
20
- class Passwords
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('passwords', :user) if path.nil?
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 set(host, user, pass)
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 = { host => { user => pass } }
62
+ @data = { key1 => { key2 => val } }
53
63
  return
54
64
  end
55
- hd = @data[host]
65
+ hd = @data[key1]
56
66
  if hd.nil? || !hd.is_a?(Hash)
57
- @data[host] = { user => pass }
67
+ @data[key1] = { key2 => val }
58
68
  return
59
69
  end
60
- @data[host][user] = pass
70
+ @data[key1][key2] = val
61
71
  end
62
72
 
63
- def get(host, user)
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?(host)
77
- return nil unless @data[host].is_a?(Hash)
78
- return nil unless @data[host].key?(user)
79
- @data[host][user]
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(host, user)
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[host]
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 password for #{fancy_ticks("#{user}@#{host}")}))
88
+ say(%(--dry: Not removing #{@val_type} for #{fancy_ticks("#{key2}@#{host}")}))
90
89
  end
91
90
  return
92
91
  end
93
- @data[host].delete(user) if hd.key?(user)
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 hosts and usernames. Does not return passwords.
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
- @data.each_pair { |key, value| ret[key] = value.keys } unless @data.nil?
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
- # MAYBE/2017-08-17:
102
- # sort_by_name(ret)
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
@@ -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
@@ -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 Http
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
@@ -5,6 +5,8 @@
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 'singleton'
9
+
8
10
  module MrMurano
9
11
  ## Track what things are syncable.
10
12
  class SyncRoot
@@ -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 Http
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.warning(
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.instance
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.instance
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
- tok = MrMurano::Account.instance.token
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 password delete <username>
45
- murano password delete <username>/twofactor
46
- murano config --unset --user user.name
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 '--token', 'Remove just the two-factor token'
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
- MrMurano::Account.instance.logout(options.token)
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 expect DEBUG
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 been saved.
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 '--from_env', %(Use password in MURANO_PASSWORD)
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: ') { |q| q.echo = '*' }
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('-y', '--[no-]yes', %(Answer "yes" to all prompts and run non-interactively))
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'])
@@ -23,7 +23,7 @@ Show readable information about the current configuration.
23
23
  c.action do |args, options|
24
24
  c.verify_arg_count!(args)
25
25
 
26
- acc = MrMurano::Account.instance
26
+ acc = MrMurano::Account.new
27
27
  biz = MrMurano::Business.new
28
28
 
29
29
  selected_business = nil
@@ -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
+