morpheus-cli 3.6.8 → 3.6.9
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/lib/morpheus/api/account_groups_interface.rb +2 -2
- data/lib/morpheus/api/accounts_interface.rb +4 -7
- data/lib/morpheus/api/api_client.rb +207 -70
- data/lib/morpheus/api/app_templates_interface.rb +7 -28
- data/lib/morpheus/api/apps_interface.rb +14 -21
- data/lib/morpheus/api/archive_buckets_interface.rb +2 -2
- data/lib/morpheus/api/archive_files_interface.rb +6 -6
- data/lib/morpheus/api/auth_interface.rb +14 -1
- data/lib/morpheus/api/blueprints_interface.rb +9 -16
- data/lib/morpheus/api/cloud_datastores_interface.rb +1 -1
- data/lib/morpheus/api/cloud_policies_interface.rb +1 -1
- data/lib/morpheus/api/clouds_interface.rb +18 -21
- data/lib/morpheus/api/cypher_interface.rb +19 -28
- data/lib/morpheus/api/file_copy_request_interface.rb +1 -1
- data/lib/morpheus/api/group_policies_interface.rb +1 -1
- data/lib/morpheus/api/groups_interface.rb +4 -4
- data/lib/morpheus/api/image_builder_boot_scripts_interface.rb +1 -1
- data/lib/morpheus/api/image_builder_image_builds_interface.rb +2 -2
- data/lib/morpheus/api/image_builder_preseed_scripts_interface.rb +1 -1
- data/lib/morpheus/api/instances_interface.rb +17 -23
- data/lib/morpheus/api/logs_interface.rb +7 -10
- data/lib/morpheus/api/network_domains_interface.rb +1 -1
- data/lib/morpheus/api/network_groups_interface.rb +1 -1
- data/lib/morpheus/api/network_pool_servers_interface.rb +1 -1
- data/lib/morpheus/api/network_pools_interface.rb +1 -1
- data/lib/morpheus/api/network_proxies_interface.rb +1 -1
- data/lib/morpheus/api/network_services_interface.rb +1 -1
- data/lib/morpheus/api/networks_interface.rb +1 -1
- data/lib/morpheus/api/old_cypher_interface.rb +55 -0
- data/lib/morpheus/api/packages_interface.rb +1 -1
- data/lib/morpheus/api/policies_interface.rb +1 -1
- data/lib/morpheus/api/setup_interface.rb +1 -1
- data/lib/morpheus/api/storage_providers_interface.rb +1 -1
- data/lib/morpheus/api/whoami_interface.rb +1 -1
- data/lib/morpheus/benchmarking.rb +277 -0
- data/lib/morpheus/cli.rb +6 -22
- data/lib/morpheus/cli/access_token_command.rb +172 -0
- data/lib/morpheus/cli/accounts.rb +5 -0
- data/lib/morpheus/cli/apps.rb +93 -37
- data/lib/morpheus/cli/archives_command.rb +0 -2
- data/lib/morpheus/cli/auth_command.rb +112 -0
- data/lib/morpheus/cli/blueprints_command.rb +50 -13
- data/lib/morpheus/cli/change_password_command.rb +148 -0
- data/lib/morpheus/cli/cli_command.rb +173 -49
- data/lib/morpheus/cli/clouds.rb +15 -5
- data/lib/morpheus/cli/command_error.rb +7 -1
- data/lib/morpheus/cli/{alias_command.rb → commands/standard/alias_command.rb} +79 -51
- data/lib/morpheus/cli/commands/standard/benchmark_command.rb +399 -0
- data/lib/morpheus/cli/commands/standard/coloring_command.rb +60 -0
- data/lib/morpheus/cli/{curl_command.rb → commands/standard/curl_command.rb} +0 -7
- data/lib/morpheus/cli/commands/standard/debug_command.rb +61 -0
- data/lib/morpheus/cli/{echo_command.rb → commands/standard/echo_command.rb} +1 -1
- data/lib/morpheus/cli/{edit_profile_command.rb → commands/standard/edit_profile_command.rb} +0 -0
- data/lib/morpheus/cli/{edit_rc_command.rb → commands/standard/edit_rc_command.rb} +0 -0
- data/lib/morpheus/cli/commands/standard/get_prompt_command.rb +39 -0
- data/lib/morpheus/cli/commands/standard/history_command.rb +76 -0
- data/lib/morpheus/cli/{log_level_command.rb → commands/standard/log_level_command.rb} +1 -1
- data/lib/morpheus/cli/{man_command.rb → commands/standard/man_command.rb} +2 -2
- data/lib/morpheus/cli/commands/standard/rm_command.rb +14 -0
- data/lib/morpheus/cli/commands/standard/set_prompt_command.rb +54 -0
- data/lib/morpheus/cli/{sleep_command.rb → commands/standard/sleep_command.rb} +0 -0
- data/lib/morpheus/cli/{source_command.rb → commands/standard/source_command.rb} +0 -0
- data/lib/morpheus/cli/{ssl_verification_command.rb → commands/standard/ssl_verification_command.rb} +1 -1
- data/lib/morpheus/cli/commands/standard/tee_command.rb +14 -0
- data/lib/morpheus/cli/{version_command.rb → commands/standard/version_command.rb} +0 -0
- data/lib/morpheus/cli/credentials.rb +276 -87
- data/lib/morpheus/cli/cypher_command.rb +333 -214
- data/lib/morpheus/cli/error_handler.rb +12 -2
- data/lib/morpheus/cli/groups.rb +44 -20
- data/lib/morpheus/cli/hosts.rb +39 -16
- data/lib/morpheus/cli/instances.rb +114 -62
- data/lib/morpheus/cli/login.rb +74 -21
- data/lib/morpheus/cli/logout.rb +3 -4
- data/lib/morpheus/cli/mixins/accounts_helper.rb +50 -18
- data/lib/morpheus/cli/mixins/print_helper.rb +207 -42
- data/lib/morpheus/cli/old_cypher_command.rb +414 -0
- data/lib/morpheus/cli/option_parser.rb +6 -1
- data/lib/morpheus/cli/processes_command.rb +3 -0
- data/lib/morpheus/cli/remote.rb +11 -17
- data/lib/morpheus/cli/roles.rb +17 -17
- data/lib/morpheus/cli/security_groups.rb +47 -17
- data/lib/morpheus/cli/shell.rb +139 -79
- data/lib/morpheus/cli/tenants_command.rb +353 -0
- data/lib/morpheus/cli/users.rb +26 -18
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whoami.rb +14 -10
- data/lib/morpheus/formatters.rb +4 -4
- data/lib/morpheus/logging.rb +16 -8
- data/lib/morpheus/terminal.rb +63 -34
- metadata +28 -15
- data/lib/morpheus/cli/coloring_command.rb +0 -45
- data/lib/morpheus/cli/set_prompt_command.rb +0 -51
|
@@ -6,9 +6,15 @@ require 'morpheus/cli/cli_command'
|
|
|
6
6
|
class Morpheus::Cli::CypherCommand
|
|
7
7
|
include Morpheus::Cli::CliCommand
|
|
8
8
|
|
|
9
|
-
set_command_name :cypher
|
|
9
|
+
set_command_name :'cypher'
|
|
10
10
|
|
|
11
|
-
register_subcommands :list, :get, :
|
|
11
|
+
register_subcommands :list, :get, :put, :remove
|
|
12
|
+
# some appropriate aliases
|
|
13
|
+
#register_subcommands :read => :get,
|
|
14
|
+
#register_subcommands :write => :put
|
|
15
|
+
#register_subcommands :add => :put
|
|
16
|
+
#register_subcommands :delete => :remove
|
|
17
|
+
# register_subcommands :destroy => :destroy
|
|
12
18
|
|
|
13
19
|
def initialize()
|
|
14
20
|
# @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
|
@@ -16,6 +22,7 @@ class Morpheus::Cli::CypherCommand
|
|
|
16
22
|
|
|
17
23
|
def connect(opts)
|
|
18
24
|
@api_client = establish_remote_appliance_connection(opts)
|
|
25
|
+
# @cypher_interface = @api_client.cypher
|
|
19
26
|
@cypher_interface = @api_client.cypher
|
|
20
27
|
end
|
|
21
28
|
|
|
@@ -27,51 +34,74 @@ class Morpheus::Cli::CypherCommand
|
|
|
27
34
|
options = {}
|
|
28
35
|
params = {}
|
|
29
36
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
30
|
-
opts.banner = subcommand_usage()
|
|
37
|
+
opts.banner = subcommand_usage("[key]")
|
|
38
|
+
# opts.on('--details', '--details', "Show more details." ) do
|
|
39
|
+
# options[:details] = true
|
|
40
|
+
# end
|
|
31
41
|
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :json, :dry_run, :remote])
|
|
32
|
-
opts.footer = "List cypher
|
|
42
|
+
opts.footer = "List cypher keys." + "\n" +
|
|
43
|
+
"[key] is optional. This is the cypher key or path to search for."
|
|
33
44
|
end
|
|
34
45
|
optparse.parse!(args)
|
|
35
46
|
connect(options)
|
|
47
|
+
if args.count > 1
|
|
48
|
+
print_error Morpheus::Terminal.angry_prompt
|
|
49
|
+
puts_error "wrong number of arguments, expected 0-1 and got #{args.count}\n#{optparse}"
|
|
50
|
+
return 1
|
|
51
|
+
end
|
|
52
|
+
item_key = args[0]
|
|
36
53
|
begin
|
|
37
54
|
params.merge!(parse_list_options(options))
|
|
55
|
+
@cypher_interface.setopts(options)
|
|
38
56
|
if options[:dry_run]
|
|
39
|
-
print_dry_run @cypher_interface.dry.list(params)
|
|
57
|
+
print_dry_run @cypher_interface.dry.list(item_key, params)
|
|
40
58
|
return 0
|
|
41
59
|
end
|
|
42
|
-
json_response = @cypher_interface.list(params)
|
|
43
|
-
cypher_items = json_response["cyphers"]
|
|
60
|
+
json_response = @cypher_interface.list(item_key, params)
|
|
44
61
|
if options[:json]
|
|
45
|
-
puts as_json(json_response, options
|
|
62
|
+
puts as_json(json_response, options)
|
|
46
63
|
return 0
|
|
47
64
|
elsif options[:yaml]
|
|
48
|
-
puts as_yaml(json_response, options
|
|
65
|
+
puts as_yaml(json_response, options)
|
|
49
66
|
return 0
|
|
50
67
|
elsif options[:csv]
|
|
51
|
-
puts records_as_csv(
|
|
68
|
+
puts records_as_csv([json_response], options)
|
|
52
69
|
return 0
|
|
53
70
|
end
|
|
54
|
-
|
|
71
|
+
cypher_data = json_response["data"]
|
|
72
|
+
title = "Morpheus Cypher Key List"
|
|
55
73
|
subtitles = []
|
|
56
74
|
subtitles += parse_list_subtitles(options)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
75
|
+
if item_key
|
|
76
|
+
subtitles << "Key: #{item_key}"
|
|
77
|
+
end
|
|
78
|
+
print_h1 title, subtitles, options
|
|
79
|
+
|
|
80
|
+
cypher_keys = json_response["data"] ? json_response["data"]["keys"] : []
|
|
81
|
+
if cypher_keys.nil? || cypher_keys.empty?
|
|
82
|
+
if item_key
|
|
83
|
+
print cyan,"No cypher items found for '#{item_key}'.",reset,"\n"
|
|
84
|
+
end
|
|
60
85
|
else
|
|
86
|
+
|
|
61
87
|
cypher_columns = {
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"
|
|
88
|
+
"KEY" => lambda {|it| it["itemKey"] },
|
|
89
|
+
# "LEASE REMAINING" => lambda {|it|
|
|
90
|
+
# format_lease_remaining(it["expireDate"])
|
|
91
|
+
# },
|
|
92
|
+
"TTL" => lambda {|it|
|
|
93
|
+
format_expiration_ttl(it["expireDate"])
|
|
94
|
+
},
|
|
95
|
+
"EXPIRATION" => lambda {|it|
|
|
96
|
+
format_expiration_date(it["expireDate"])
|
|
97
|
+
},
|
|
98
|
+
"DATE CREATED" => lambda {|it| format_local_dt(it["dateCreated"]) },
|
|
99
|
+
"LAST ACCESS" => lambda {|it| format_local_dt(it["lastAccessed"]) }
|
|
67
100
|
}
|
|
68
|
-
if options[:include_fields]
|
|
69
|
-
cypher_columns = options[:include_fields]
|
|
70
|
-
end
|
|
71
101
|
print cyan
|
|
72
|
-
print as_pretty_table(
|
|
102
|
+
print as_pretty_table(json_response["cypherItems"], cypher_columns, options)
|
|
73
103
|
print reset
|
|
74
|
-
print_results_pagination(
|
|
104
|
+
print_results_pagination({size:cypher_keys.size,total:cypher_keys.size.to_i})
|
|
75
105
|
end
|
|
76
106
|
print reset,"\n"
|
|
77
107
|
return 0
|
|
@@ -83,15 +113,33 @@ class Morpheus::Cli::CypherCommand
|
|
|
83
113
|
|
|
84
114
|
def get(args)
|
|
85
115
|
options = {}
|
|
116
|
+
params = {}
|
|
117
|
+
value_only = false
|
|
86
118
|
do_decrypt = false
|
|
119
|
+
item_ttl = nil
|
|
87
120
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
88
|
-
opts.banner = subcommand_usage("[
|
|
89
|
-
opts.on(nil, '--decrypt', 'Display the decrypted value') do
|
|
90
|
-
|
|
121
|
+
opts.banner = subcommand_usage("[key]")
|
|
122
|
+
# opts.on(nil, '--decrypt', 'Display the decrypted value') do
|
|
123
|
+
# do_decrypt = true
|
|
124
|
+
# end
|
|
125
|
+
# opts.on(nil, '--metadata', 'Display metadata about the key, such as versions.') do
|
|
126
|
+
# display_versions = true
|
|
127
|
+
# end
|
|
128
|
+
opts.on('-v', '--value', 'Print only the decrypted value.') do
|
|
129
|
+
value_only = true
|
|
130
|
+
end
|
|
131
|
+
opts.on( '-t', '--ttl SECONDS', "Time to live, the lease duration before this key expires. Use if creating new key." ) do |val|
|
|
132
|
+
item_ttl = val
|
|
133
|
+
if val.to_s.empty? || val.to_s == '0'
|
|
134
|
+
item_ttl = 0
|
|
135
|
+
else
|
|
136
|
+
item_ttl = val
|
|
137
|
+
end
|
|
91
138
|
end
|
|
92
|
-
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
|
93
|
-
opts.footer = "
|
|
94
|
-
"[
|
|
139
|
+
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :quiet, :remote])
|
|
140
|
+
opts.footer = "Read a cypher item and display the decrypted value." + "\n" +
|
|
141
|
+
"[key] is required. This is the cypher key to read." + "\n" +
|
|
142
|
+
"Use --ttl to specify a ttl if expecting cypher engine to automatically create the key."
|
|
95
143
|
end
|
|
96
144
|
optparse.parse!(args)
|
|
97
145
|
if args.count != 1
|
|
@@ -101,105 +149,98 @@ class Morpheus::Cli::CypherCommand
|
|
|
101
149
|
end
|
|
102
150
|
connect(options)
|
|
103
151
|
begin
|
|
152
|
+
item_key = args[0]
|
|
153
|
+
if item_ttl
|
|
154
|
+
params["ttl"] = item_ttl
|
|
155
|
+
end
|
|
156
|
+
@cypher_interface.setopts(options)
|
|
104
157
|
if options[:dry_run]
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
else
|
|
108
|
-
print_dry_run @cypher_interface.dry.list({name:args[0]})
|
|
109
|
-
end
|
|
110
|
-
return
|
|
158
|
+
print_dry_run @cypher_interface.dry.get(item_key, params)
|
|
159
|
+
return 0
|
|
111
160
|
end
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if do_decrypt
|
|
117
|
-
decrypt_json_response = @cypher_interface.decrypt(cypher_item["id"])
|
|
161
|
+
json_response = @cypher_interface.get(item_key, params)
|
|
162
|
+
|
|
163
|
+
if options[:quiet]
|
|
164
|
+
return 0
|
|
118
165
|
end
|
|
119
|
-
|
|
120
|
-
cypher_item = json_response['cypher']
|
|
166
|
+
|
|
121
167
|
if options[:json]
|
|
122
|
-
puts as_json(json_response, options
|
|
168
|
+
puts as_json(json_response, options)
|
|
123
169
|
return 0
|
|
124
170
|
elsif options[:yaml]
|
|
125
|
-
puts as_yaml(json_response, options
|
|
171
|
+
puts as_yaml(json_response, options)
|
|
126
172
|
return 0
|
|
127
173
|
elsif options[:csv]
|
|
128
|
-
puts records_as_csv([
|
|
174
|
+
puts records_as_csv([json_response], options)
|
|
175
|
+
return 0
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
cypher_item = json_response['cypher']
|
|
179
|
+
decrypted_value = json_response["data"]
|
|
180
|
+
|
|
181
|
+
if value_only
|
|
182
|
+
print cyan
|
|
183
|
+
if decrypted_value.is_a?(Hash)
|
|
184
|
+
puts as_json(decrypted_value)
|
|
185
|
+
else
|
|
186
|
+
puts decrypted_value.to_s
|
|
187
|
+
end
|
|
188
|
+
print reset
|
|
129
189
|
return 0
|
|
130
190
|
end
|
|
131
|
-
|
|
191
|
+
|
|
192
|
+
print_h1 "Cypher Key", [], options
|
|
132
193
|
print cyan
|
|
194
|
+
# This response does contain cypher too though.
|
|
195
|
+
|
|
196
|
+
if cypher_item.empty?
|
|
197
|
+
puts_error "Cypher data not found in response"
|
|
198
|
+
return 1
|
|
199
|
+
end
|
|
133
200
|
description_cols = {
|
|
134
|
-
"ID" => 'id',
|
|
135
|
-
# what here
|
|
201
|
+
#"ID" => 'id',
|
|
136
202
|
"Key" => lambda {|it| it["itemKey"] },
|
|
137
|
-
|
|
138
|
-
|
|
203
|
+
"TTL" => lambda {|it|
|
|
204
|
+
format_expiration_ttl(it["expireDate"])
|
|
205
|
+
},
|
|
206
|
+
"Expiration" => lambda {|it|
|
|
207
|
+
format_expiration_date(it["expireDate"])
|
|
208
|
+
},
|
|
139
209
|
"Date Created" => lambda {|it| format_local_dt(it["dateCreated"]) },
|
|
140
|
-
"Last
|
|
210
|
+
"Last Access" => lambda {|it| format_local_dt(it["lastAccessed"]) }
|
|
141
211
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
print_h2 "Decrypted Value"
|
|
145
|
-
print cyan
|
|
146
|
-
puts decrypt_json_response["cypher"] ? decrypt_json_response["cypher"]["itemValue"] : ""
|
|
212
|
+
if cypher_item["expireDate"].nil?
|
|
213
|
+
description_cols.delete("Expires")
|
|
147
214
|
end
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return 0
|
|
151
|
-
rescue RestClient::Exception => e
|
|
152
|
-
print_rest_exception(e, options)
|
|
153
|
-
return 1
|
|
154
|
-
end
|
|
155
|
-
end
|
|
215
|
+
print_description_list(description_cols, cypher_item)
|
|
156
216
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
options = {}
|
|
160
|
-
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
161
|
-
opts.banner = subcommand_usage("[id]")
|
|
162
|
-
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
|
163
|
-
opts.footer = "Decrypt the value of a cypher." + "\n" +
|
|
164
|
-
"[id] is required. This is the id or key of a cypher."
|
|
165
|
-
end
|
|
166
|
-
optparse.parse!(args)
|
|
167
|
-
if args.count != 1
|
|
168
|
-
print_error Morpheus::Terminal.angry_prompt
|
|
169
|
-
puts_error "wrong number of arguments, expected 1 and got #{args.count}\n#{optparse}"
|
|
170
|
-
return 1
|
|
171
|
-
end
|
|
172
|
-
connect(options)
|
|
173
|
-
begin
|
|
174
|
-
cypher_item = find_cypher_by_name_or_id(args[0])
|
|
175
|
-
return 1 if cypher_item.nil?
|
|
176
|
-
if options[:dry_run]
|
|
177
|
-
print_dry_run @cypher_interface.dry.decrypt(cypher_item["id"], params)
|
|
178
|
-
return
|
|
179
|
-
end
|
|
217
|
+
print_h2 "Value", options
|
|
218
|
+
# print_h2 "Decrypted Value"
|
|
180
219
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
220
|
+
if decrypted_value
|
|
221
|
+
print cyan
|
|
222
|
+
if decrypted_value.is_a?(String)
|
|
223
|
+
# attempt to parse and render as_json
|
|
224
|
+
if decrypted_value.to_s[0] == '{' && decrypted_value.to_s[-1] == '}'
|
|
225
|
+
begin
|
|
226
|
+
json_value = JSON.parse(decrypted_value)
|
|
227
|
+
puts as_json(json_value)
|
|
228
|
+
rescue => ex
|
|
229
|
+
Morpheus::Logging::DarkPrinter.puts "Failed to parse cypher value '#{decrypted_value}' as JSON. Error: #{ex}" if Morpheus::Logging.debug?
|
|
230
|
+
puts decrypted_value
|
|
231
|
+
end
|
|
232
|
+
else
|
|
233
|
+
puts decrypted_value
|
|
234
|
+
end
|
|
235
|
+
else
|
|
236
|
+
puts as_json(decrypted_value)
|
|
237
|
+
end
|
|
238
|
+
else
|
|
239
|
+
puts "No data found."
|
|
194
240
|
end
|
|
195
|
-
|
|
196
|
-
print cyan
|
|
197
|
-
print_description_list({
|
|
198
|
-
"ID" => 'id',
|
|
199
|
-
"Key" => lambda {|it| it["itemKey"] },
|
|
200
|
-
"Value" => lambda {|it| it["itemValue"] }
|
|
201
|
-
}, json_response["cypher"])
|
|
241
|
+
|
|
202
242
|
print reset, "\n"
|
|
243
|
+
|
|
203
244
|
return 0
|
|
204
245
|
rescue RestClient::Exception => e
|
|
205
246
|
print_rest_exception(e, options)
|
|
@@ -207,83 +248,150 @@ class Morpheus::Cli::CypherCommand
|
|
|
207
248
|
end
|
|
208
249
|
end
|
|
209
250
|
|
|
210
|
-
def
|
|
251
|
+
def put(args)
|
|
211
252
|
options = {}
|
|
212
253
|
params = {}
|
|
213
|
-
|
|
254
|
+
item_key = nil
|
|
255
|
+
item_value = nil
|
|
256
|
+
item_ttl = nil
|
|
257
|
+
no_overwrite = nil
|
|
214
258
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
215
|
-
opts.banner = subcommand_usage()
|
|
216
|
-
opts.on('--key VALUE', String, "Key
|
|
217
|
-
|
|
259
|
+
opts.banner = subcommand_usage("[key] [value]")
|
|
260
|
+
# opts.on( '--key VALUE', String, "Key" ) do |val|
|
|
261
|
+
# item_key = val
|
|
262
|
+
# end
|
|
263
|
+
opts.on( '-v', '--value VALUE', "Secret value" ) do |val|
|
|
264
|
+
item_value = val
|
|
218
265
|
end
|
|
219
|
-
|
|
220
|
-
|
|
266
|
+
opts.on( '-t', '--ttl SECONDS', "Time to live, the lease duration before this key expires." ) do |val|
|
|
267
|
+
item_ttl = val
|
|
268
|
+
if val.to_s.empty? || val.to_s == '0'
|
|
269
|
+
item_ttl = 0
|
|
270
|
+
else
|
|
271
|
+
item_ttl = val
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
# opts.on( '--no-overwrite', '--no-overwrite', "Do not overwrite existing keys. Existing keys are overwritten by default." ) do
|
|
275
|
+
# params['overwrite'] = false
|
|
276
|
+
# end
|
|
277
|
+
build_common_options(opts, options, [:auto_confirm, :options, :payload, :json, :dry_run, :quiet, :remote])
|
|
278
|
+
opts.footer = "Create or update a cypher key." + "\n" +
|
|
279
|
+
"[key] is required. This is the key of the cypher being created or updated." + "\n" +
|
|
280
|
+
"[value] is required. This is the new value or value pairs being stored. Supports format foo=bar, 1-N arguments." + "\n" +
|
|
281
|
+
"The --payload option can be used instead of passing [value] argument."
|
|
221
282
|
end
|
|
222
283
|
optparse.parse!(args)
|
|
223
|
-
if args.count
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
end
|
|
284
|
+
# if args.count < 1
|
|
285
|
+
# print_error Morpheus::Terminal.angry_prompt
|
|
286
|
+
# puts_error "wrong number of arguments, expected 1-N and got #{args.count}\n#{optparse}"
|
|
287
|
+
# return 1
|
|
288
|
+
# end
|
|
228
289
|
connect(options)
|
|
229
290
|
begin
|
|
291
|
+
if args[0]
|
|
292
|
+
item_key = args[0]
|
|
293
|
+
end
|
|
294
|
+
options[:options] ||= {}
|
|
295
|
+
options[:options]['key'] = item_key if item_key
|
|
296
|
+
# Key prompt
|
|
297
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'key', 'fieldLabel' => 'Key', 'type' => 'text', 'required' => true, 'description' => cypher_key_help}], options[:options])
|
|
298
|
+
item_key = v_prompt['key']
|
|
299
|
+
|
|
230
300
|
payload = nil
|
|
231
301
|
if options[:payload]
|
|
232
302
|
payload = options[:payload]
|
|
303
|
+
payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) || ['key','value'].include?(k)}) if options[:options] && options[:options].keys.size > 0
|
|
233
304
|
else
|
|
234
305
|
# merge -O options into normally parsed options
|
|
235
|
-
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options] && options[:options].keys.size > 0
|
|
306
|
+
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) || ['key','value'].include?(k)}) if options[:options] && options[:options].keys.size > 0
|
|
236
307
|
|
|
237
|
-
#
|
|
238
|
-
if args[0]
|
|
239
|
-
params['itemKey'] = args[0]
|
|
240
|
-
end
|
|
241
|
-
# Key
|
|
242
|
-
if !params['itemKey']
|
|
243
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'itemKey', 'fieldLabel' => 'Key', 'type' => 'text', 'required' => true, 'description' => cypher_key_help}], options)
|
|
244
|
-
params['itemKey'] = v_prompt['itemKey']
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
# Value
|
|
308
|
+
# Value prompt
|
|
248
309
|
value_is_required = false
|
|
249
|
-
cypher_mount_type =
|
|
250
|
-
if
|
|
310
|
+
cypher_mount_type = item_key.split("/").first
|
|
311
|
+
if ["secret"].include?(cypher_mount_type)
|
|
251
312
|
value_is_required = true
|
|
252
313
|
end
|
|
253
314
|
|
|
254
|
-
|
|
255
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'itemValue', 'fieldLabel' => 'Value', 'type' => 'text', 'required' => value_is_required, 'description' => "Value for this cypher"}], options)
|
|
256
|
-
params['itemValue'] = v_prompt['itemValue']
|
|
257
|
-
end
|
|
315
|
+
# todo: read value from STDIN shall we?
|
|
258
316
|
|
|
259
|
-
#
|
|
260
|
-
if
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
317
|
+
# cool, we got value as arguments like foo=bar
|
|
318
|
+
if args.count > 1
|
|
319
|
+
# parse one and only arg as the value like password/mine mypassword123
|
|
320
|
+
if args.count == 2 && args[1].split("=").size() == 1
|
|
321
|
+
item_value = args[1]
|
|
322
|
+
elsif args.count > 1
|
|
323
|
+
# parse args as key value pairs like secret/config foo=bar thing=myvalue
|
|
324
|
+
value_arguments = args[1..-1]
|
|
325
|
+
value_arguments_map = {}
|
|
326
|
+
value_arguments.each do |value_argument|
|
|
327
|
+
value_pair = value_argument.split("=")
|
|
328
|
+
value_arguments_map[value_pair[0]] = value_pair[1] ? value_pair[1..-1].join("=") : nil
|
|
329
|
+
end
|
|
330
|
+
item_value = value_arguments_map
|
|
331
|
+
end
|
|
332
|
+
else
|
|
333
|
+
# Prompt for a single text value to be sent as {"value":"my secret"}
|
|
334
|
+
if value_is_required
|
|
335
|
+
options[:options]['value'] = item_value if item_value
|
|
336
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'value', 'fieldLabel' => 'Value', 'type' => 'text', 'required' => value_is_required, 'description' => "Secret value for this cypher"}], options[:options])
|
|
337
|
+
item_value = v_prompt['value']
|
|
338
|
+
end
|
|
266
339
|
end
|
|
267
340
|
|
|
268
341
|
# construct payload
|
|
269
|
-
payload = {
|
|
270
|
-
|
|
271
|
-
}
|
|
342
|
+
# payload = {
|
|
343
|
+
# 'cypher' => params
|
|
344
|
+
# }
|
|
345
|
+
|
|
346
|
+
# if value is valid json, then the payload IS the value
|
|
347
|
+
if item_value.is_a?(String) && item_value.to_s[0] == '{' && item_value.to_s[-1] == '}'
|
|
348
|
+
begin
|
|
349
|
+
json_object = JSON.parse(item_value)
|
|
350
|
+
item_value = json_object
|
|
351
|
+
rescue => ex
|
|
352
|
+
Morpheus::Logging::DarkPrinter.puts "Failed to parse cypher value '#{item_value}' as JSON. Error: #{ex}" if Morpheus::Logging.debug?
|
|
353
|
+
raise_command_error "Failed to parse cypher value as JSON: #{item_value}"
|
|
354
|
+
# return 1
|
|
355
|
+
end
|
|
356
|
+
else
|
|
357
|
+
# it is just a string
|
|
358
|
+
if item_value.is_a?(String)
|
|
359
|
+
payload = {"value" => item_value}
|
|
360
|
+
elsif item_value.nil?
|
|
361
|
+
payload = {}
|
|
362
|
+
else item_value
|
|
363
|
+
# great, a Hash I hope
|
|
364
|
+
payload = item_value
|
|
365
|
+
end
|
|
366
|
+
end
|
|
272
367
|
end
|
|
273
368
|
|
|
369
|
+
# prompt for Lease
|
|
370
|
+
options[:options]['ttl'] = item_ttl if item_ttl
|
|
371
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'ttl', 'fieldLabel' => 'Lease (TTL in seconds)', 'type' => 'text', 'required' => false, 'description' => cypher_ttl_help}], options[:options])
|
|
372
|
+
item_ttl = v_prompt['ttl']
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
if item_ttl
|
|
376
|
+
# I would like this better as params...
|
|
377
|
+
payload["ttl"] = item_ttl
|
|
378
|
+
end
|
|
379
|
+
@cypher_interface.setopts(options)
|
|
274
380
|
if options[:dry_run]
|
|
275
|
-
print_dry_run @cypher_interface.dry.create(payload)
|
|
381
|
+
print_dry_run @cypher_interface.dry.create(item_key, payload)
|
|
276
382
|
return
|
|
277
383
|
end
|
|
278
|
-
|
|
384
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to overwrite the cypher key #{item_key}?", {default:true})
|
|
385
|
+
return 9, "aborted command"
|
|
386
|
+
end
|
|
387
|
+
json_response = @cypher_interface.create(item_key, payload)
|
|
279
388
|
if options[:json]
|
|
280
|
-
|
|
281
|
-
print "\n"
|
|
389
|
+
puts as_json(json_response, options)
|
|
282
390
|
elsif !options[:quiet]
|
|
283
|
-
print_green_success "
|
|
284
|
-
#
|
|
391
|
+
print_green_success "Wrote cypher #{item_key}"
|
|
392
|
+
# should print without doing get, because that can use a token.
|
|
285
393
|
cypher_item = json_response['cypher']
|
|
286
|
-
get([
|
|
394
|
+
get([item_key])
|
|
287
395
|
end
|
|
288
396
|
return 0
|
|
289
397
|
rescue RestClient::Exception => e
|
|
@@ -292,19 +400,14 @@ class Morpheus::Cli::CypherCommand
|
|
|
292
400
|
end
|
|
293
401
|
end
|
|
294
402
|
|
|
295
|
-
# def update(args)
|
|
296
|
-
# end
|
|
297
|
-
|
|
298
|
-
# def decrypt(args)
|
|
299
|
-
# end
|
|
300
|
-
|
|
301
403
|
def remove(args)
|
|
302
404
|
options = {}
|
|
405
|
+
params = {}
|
|
303
406
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
304
|
-
opts.banner = subcommand_usage("[
|
|
305
|
-
build_common_options(opts, options, [:
|
|
407
|
+
opts.banner = subcommand_usage("[key]")
|
|
408
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
|
306
409
|
opts.footer = "Delete a cypher." + "\n" +
|
|
307
|
-
"[
|
|
410
|
+
"[key] is required. This is the key of a cypher."
|
|
308
411
|
end
|
|
309
412
|
optparse.parse!(args)
|
|
310
413
|
|
|
@@ -316,21 +419,22 @@ class Morpheus::Cli::CypherCommand
|
|
|
316
419
|
|
|
317
420
|
connect(options)
|
|
318
421
|
begin
|
|
319
|
-
|
|
422
|
+
item_key = args[0]
|
|
423
|
+
cypher_item = find_cypher_by_key(item_key)
|
|
320
424
|
return 1 if cypher_item.nil?
|
|
321
|
-
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the cypher #{
|
|
425
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the cypher #{item_key}?")
|
|
322
426
|
return 9, "aborted command"
|
|
323
427
|
end
|
|
428
|
+
@cypher_interface.setopts(options)
|
|
324
429
|
if options[:dry_run]
|
|
325
|
-
print_dry_run @cypher_interface.dry.destroy(
|
|
430
|
+
print_dry_run @cypher_interface.dry.destroy(item_key, params)
|
|
326
431
|
return
|
|
327
432
|
end
|
|
328
|
-
json_response = @cypher_interface.destroy(
|
|
433
|
+
json_response = @cypher_interface.destroy(item_key, params)
|
|
329
434
|
if options[:json]
|
|
330
|
-
|
|
331
|
-
print "\n"
|
|
435
|
+
puts as_json(json_response, options)
|
|
332
436
|
elsif !options[:quiet]
|
|
333
|
-
print_green_success "Deleted cypher #{
|
|
437
|
+
print_green_success "Deleted cypher #{item_key}"
|
|
334
438
|
# list([])
|
|
335
439
|
end
|
|
336
440
|
return 0
|
|
@@ -342,22 +446,13 @@ class Morpheus::Cli::CypherCommand
|
|
|
342
446
|
|
|
343
447
|
private
|
|
344
448
|
|
|
345
|
-
def
|
|
346
|
-
if val.to_s =~ /\A\d{1,}\Z/
|
|
347
|
-
return find_cypher_by_id(val)
|
|
348
|
-
else
|
|
349
|
-
return find_cypher_by_name(val)
|
|
350
|
-
end
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
def find_cypher_by_id(id)
|
|
449
|
+
def find_cypher_by_key(key, params={})
|
|
354
450
|
begin
|
|
355
|
-
|
|
356
|
-
json_response
|
|
357
|
-
return json_response['cypher']
|
|
451
|
+
json_response = @cypher_interface.get(key, params)
|
|
452
|
+
return json_response
|
|
358
453
|
rescue RestClient::Exception => e
|
|
359
454
|
if e.response && e.response.code == 404
|
|
360
|
-
print_red_alert "Cypher not found by
|
|
455
|
+
print_red_alert "Cypher not found by key #{key}"
|
|
361
456
|
return nil
|
|
362
457
|
else
|
|
363
458
|
raise e
|
|
@@ -365,28 +460,6 @@ class Morpheus::Cli::CypherCommand
|
|
|
365
460
|
end
|
|
366
461
|
end
|
|
367
462
|
|
|
368
|
-
def find_cypher_by_name(name)
|
|
369
|
-
# api supports name as alias for itemKey
|
|
370
|
-
json_response = @cypher_interface.list({name: name.to_s})
|
|
371
|
-
|
|
372
|
-
cypher_items = json_response['cyphers']
|
|
373
|
-
if cypher_items.empty?
|
|
374
|
-
print_red_alert "Cypher not found by name #{name}"
|
|
375
|
-
return nil
|
|
376
|
-
elsif cypher_items.size > 1
|
|
377
|
-
print_red_alert "#{cypher_items.size} cyphers found by name #{name}"
|
|
378
|
-
rows = cypher_items.collect do |cypher_item|
|
|
379
|
-
{id: cypher_item['id'], name: cypher_item['name']}
|
|
380
|
-
end
|
|
381
|
-
print red
|
|
382
|
-
print as_pretty_table(rows, [:id, :name])
|
|
383
|
-
print reset,"\n"
|
|
384
|
-
return nil
|
|
385
|
-
else
|
|
386
|
-
return cypher_items[0]
|
|
387
|
-
end
|
|
388
|
-
end
|
|
389
|
-
|
|
390
463
|
def cypher_key_help
|
|
391
464
|
"""
|
|
392
465
|
Keys can have different behaviors depending on the specified mountpoint.
|
|
@@ -398,15 +471,61 @@ uuid - Returns a new UUID by key name when requested and stores the generated UU
|
|
|
398
471
|
key - Generates a Base 64 encoded AES Key of specified bit length in the key pattern (i.e. key/128/mykey generates a 128-bit key)"""
|
|
399
472
|
end
|
|
400
473
|
|
|
401
|
-
def
|
|
474
|
+
def cypher_ttl_help
|
|
402
475
|
"""
|
|
403
|
-
Lease time in
|
|
404
|
-
Quick
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
476
|
+
Lease time in seconds (defaults to 32 days)
|
|
477
|
+
Quick Second Time Reference:
|
|
478
|
+
Hour: 3600
|
|
479
|
+
Day: 86400
|
|
480
|
+
Week: 604800
|
|
481
|
+
Month (30 days): 2592000
|
|
482
|
+
Year: 31536000
|
|
483
|
+
This can also be passed in abbreviated format with the unit as the suffix. eg. 32d, 90s, 5y
|
|
484
|
+
"""
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def format_lease_remaining(expire_date, warning_threshold=3600, return_color=cyan)
|
|
488
|
+
out = ""
|
|
489
|
+
if expire_date
|
|
490
|
+
out << format_expiration_date(expire_date, warning_threshold, return_color)
|
|
491
|
+
out << " ("
|
|
492
|
+
out << format_expiration_ttl(expire_date, warning_threshold, return_color)
|
|
493
|
+
out << ")"
|
|
494
|
+
else
|
|
495
|
+
# out << return_color
|
|
496
|
+
out << "Never expires"
|
|
497
|
+
end
|
|
498
|
+
return out
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def format_expiration_date(expire_date, warning_threshold=3600, return_color=cyan)
|
|
502
|
+
expire_date = parse_time(expire_date)
|
|
503
|
+
if !expire_date
|
|
504
|
+
# return ""
|
|
505
|
+
return cyan + "Never expires" + return_color
|
|
506
|
+
end
|
|
507
|
+
if expire_date <= Time.now
|
|
508
|
+
return red + format_local_dt(expire_date) + return_color
|
|
509
|
+
else
|
|
510
|
+
return cyan + format_local_dt(expire_date) + return_color
|
|
511
|
+
end
|
|
409
512
|
end
|
|
410
513
|
|
|
514
|
+
def format_expiration_ttl(expire_date, warning_threshold=3600, return_color=cyan)
|
|
515
|
+
expire_date = parse_time(expire_date)
|
|
516
|
+
if !expire_date
|
|
517
|
+
return ""
|
|
518
|
+
#return cyan + "Never expires" + return_color
|
|
519
|
+
end
|
|
520
|
+
seconds = expire_date - Time.now
|
|
521
|
+
if seconds <= 0
|
|
522
|
+
# return red + "Expired" + return_color
|
|
523
|
+
return red + "Expired " + format_duration_seconds(seconds.abs).to_s + " ago" + return_color
|
|
524
|
+
elsif seconds <= warning_threshold
|
|
525
|
+
return yellow + format_duration_seconds(seconds.abs).to_s + return_color
|
|
526
|
+
else
|
|
527
|
+
return cyan + format_duration_seconds(seconds.abs).to_s + return_color
|
|
528
|
+
end
|
|
529
|
+
end
|
|
411
530
|
end
|
|
412
531
|
|