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.
@@ -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
+