conjur-cli 4.24.0 → 4.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/PUBLISH.md +26 -0
- data/lib/conjur/authn.rb +2 -0
- data/lib/conjur/cli.rb +2 -2
- data/lib/conjur/command.rb +231 -2
- data/lib/conjur/command/bootstrap.rb +74 -8
- data/lib/conjur/command/groups.rb +36 -10
- data/lib/conjur/command/hosts.rb +5 -3
- data/lib/conjur/command/pubkeys.rb +25 -3
- data/lib/conjur/command/resources.rb +14 -4
- data/lib/conjur/command/rspec/mock_services.rb +3 -0
- data/lib/conjur/command/users.rb +53 -15
- data/lib/conjur/command/variables.rb +47 -77
- data/lib/conjur/version.rb +1 -1
- data/spec/command/pubkeys_spec.rb +2 -0
- data/spec/command/resources_spec.rb +10 -0
- data/spec/command/variables_spec.rb +61 -26
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9327368d238b90717af3151f2fc1a2091ff4f051
|
4
|
+
data.tar.gz: a90f6d8e898919557b9b20f54435a5b07d508f61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3b6e29c9c849478d5a67e50d0d59b6e5973dced0d58010d00fbdd1c2dc5287911a9f308c5b9ba9bc6320ea6fedfcb9e098b53582227b1231e95b3693d2a6bb1
|
7
|
+
data.tar.gz: a7a9a8fb315d6fd1dd089e6e0e3cd2330ea98f8ccb37f555eea056d09a5d258660b9b6f793f475951390d2de0c597e01ec04f339dd2e4a670d14c78e93b526be
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
#
|
1
|
+
# 4.25.0
|
2
|
+
|
3
|
+
* A record can be retired to a specific role, in addition to the default behavior of retiring to the `attic` user.
|
4
|
+
* Variable can be created with the id only, without becoming interactive
|
5
|
+
* Run `conjur variable create -i -a` to create interactively with annotations
|
6
|
+
* Interactive annotation can be performed on bare resources with `conjur resource annotate -i`.
|
7
|
+
* Don't require 'admin' user to bootstrap, prompt to create a new security admin during bootstrap
|
8
|
+
* Check if user privileges are sufficient before running `retire`
|
9
|
+
* Don't revoke a user's access to a record in the middle of retire, because doing so leads to 403 errors later on.
|
10
|
+
* Interactive mode of user, group and pubkey creation
|
2
11
|
|
3
12
|
# 4.24.0
|
4
13
|
|
data/PUBLISH.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Publishing the CLI
|
2
|
+
|
3
|
+
We distribute the Conjur CLI as a package for Ubuntu, Centos, OSX and also as a rubygem.
|
4
|
+
|
5
|
+
Steps to publish a new version of the CLI:
|
6
|
+
|
7
|
+
1. Update `VERSION` in [lib/conjur/version.rb](lib/conjur/version.rb)
|
8
|
+
2. Update the [CHANGELOG.md](CHANGELOG.md) with any changes for this version
|
9
|
+
3. Commit these changes with the message `"v#{VERSION}"`, where `VERSION` = the new version
|
10
|
+
4. Go to the specific build page for the commit [in Jenkins](https://jenkins.conjur.net/job/cli-ruby/)
|
11
|
+
5. In the left sidebar, open `Promotion Status`
|
12
|
+
6. Click `Approve` for the "rubygems" promotion and wait for it to finish
|
13
|
+
7. Click `Approve` for the "packages" promotion, this will kick off the [omnibus-conjur](https://jenkins.conjur.net/job/omnibus-conjur/) build flow [1](#ref1).
|
14
|
+
8. Download the [deb](https://jenkins.conjur.net/job/omnibus-conjur-ubuntu/), [rpm](https://jenkins.conjur.net/job/omnibus-conjur-centos/) and [pkg](https://jenkins.conjur.net/job/omnibus-conjur-osx/) packages from their build pages in Jenkins.
|
15
|
+
9. Move the downloaded files to the `pkg` folder in your local [omnibus-conjur](https://github.com/conjurinc/omnibus-conjur) project.
|
16
|
+
10. In the `omnibus-conjur` project, upload each file to S3 with `./publish pkg/<filename>`.
|
17
|
+
11. Update the links on the [CLI page](https://github.com/conjurinc/developer-www/blob/master/app/views/pages/cli/index.html.haml) for the devsite.
|
18
|
+
12. Promote the devsite to production [in Jenkins](https://jenkins.conjur.net/job/developer-www/) [2](#ref2).
|
19
|
+
|
20
|
+
---
|
21
|
+
|
22
|
+
<a id="ref1">1</a>:
|
23
|
+
The packages promotion depends on the new gem version being published.
|
24
|
+
|
25
|
+
<a id="ref2">2</a>
|
26
|
+
After deploy it will take a few minutes for the cache to update.
|
data/lib/conjur/authn.rb
CHANGED
data/lib/conjur/cli.rb
CHANGED
@@ -113,12 +113,12 @@ module Conjur
|
|
113
113
|
group = Conjur::Command.api.group(as_group)
|
114
114
|
role = Conjur::Command.api.role(group.roleid)
|
115
115
|
exit_now!("Group '#{as_group}' doesn't exist, or you don't have permission to use it") unless role.exists?
|
116
|
-
options[:
|
116
|
+
options[:ownerid] = group.roleid
|
117
117
|
end
|
118
118
|
if as_role = options.delete(:"as-role")
|
119
119
|
role = Conjur::Command.api.role(as_role)
|
120
120
|
exit_now!("Role '#{as_role}' does not exist, or you don't have permission to use it") unless role.exists?
|
121
|
-
options[:
|
121
|
+
options[:ownerid] = role.roleid
|
122
122
|
end
|
123
123
|
|
124
124
|
true
|
data/lib/conjur/command.rb
CHANGED
@@ -18,6 +18,8 @@
|
|
18
18
|
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
|
+
require 'base64'
|
22
|
+
|
21
23
|
module Conjur
|
22
24
|
class Command
|
23
25
|
extend Conjur::IdentifierManipulation
|
@@ -42,13 +44,23 @@ module Conjur
|
|
42
44
|
def api
|
43
45
|
@@api ||= Conjur::Authn.connect
|
44
46
|
end
|
47
|
+
|
48
|
+
def current_user
|
49
|
+
username = api.username
|
50
|
+
kind, id = username.split('/')
|
51
|
+
unless kind && id
|
52
|
+
id = kind
|
53
|
+
kind = 'user'
|
54
|
+
end
|
55
|
+
api.send(kind, username)
|
56
|
+
end
|
45
57
|
|
46
58
|
# Prevent a deprecated command from being displayed in the help output
|
47
59
|
def hide_docs(command)
|
48
60
|
def command.nodoc; true end
|
49
61
|
end
|
50
62
|
|
51
|
-
def acting_as_option
|
63
|
+
def acting_as_option command
|
52
64
|
return if command.flags.member?(:"as-group") # avoid duplicate flags
|
53
65
|
command.arg_name 'Perform all actions as the specified Group'
|
54
66
|
command.flag [:"as-group"]
|
@@ -57,6 +69,45 @@ module Conjur
|
|
57
69
|
command.flag [:"as-role"]
|
58
70
|
end
|
59
71
|
|
72
|
+
def interactive_option command
|
73
|
+
command.arg_name 'interactive'
|
74
|
+
command.desc 'Create variable interactively'
|
75
|
+
command.switch [:i, :'interactive']
|
76
|
+
end
|
77
|
+
|
78
|
+
def annotate_option command
|
79
|
+
command.arg_name 'annotate'
|
80
|
+
command.desc 'Add variable annotations interactively'
|
81
|
+
command.switch [:a, :annotate]
|
82
|
+
end
|
83
|
+
|
84
|
+
def prompt_for_annotations
|
85
|
+
highline.say('Add annotations (a name and value for each one):')
|
86
|
+
{}.tap do |annotations|
|
87
|
+
until (name = highline.ask(' annotation name (press enter to quit annotations): ')).empty?
|
88
|
+
annotations[name] = read_till_eof(' annotation value (^D on its own line to finish):')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def highline
|
94
|
+
require 'highline'
|
95
|
+
@highline ||= HighLine.new($stdin,$stderr)
|
96
|
+
end
|
97
|
+
|
98
|
+
def read_till_eof(prompt = nil)
|
99
|
+
highline.say(prompt) if prompt
|
100
|
+
[].tap do |lines|
|
101
|
+
loop do
|
102
|
+
begin
|
103
|
+
lines << highline.ask('')
|
104
|
+
rescue EOFError
|
105
|
+
break
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end.join("\n")
|
109
|
+
end
|
110
|
+
|
60
111
|
def command_options_for_list(c)
|
61
112
|
return if c.flags.member?(:role) # avoid duplicate flags
|
62
113
|
c.desc "Role to act as. By default, the current logged-in role is used."
|
@@ -100,6 +151,53 @@ module Conjur
|
|
100
151
|
end
|
101
152
|
end
|
102
153
|
|
154
|
+
def validate_privileges message, &block
|
155
|
+
valid = begin
|
156
|
+
yield
|
157
|
+
rescue RestClient::Forbidden
|
158
|
+
false
|
159
|
+
end
|
160
|
+
exit_now! message unless valid
|
161
|
+
end
|
162
|
+
|
163
|
+
def retire_options command
|
164
|
+
command.arg_name 'role'
|
165
|
+
command.desc "Specify a role to give the retired record to (default: the 'attic' user)"
|
166
|
+
command.long_desc %Q(When retired, all a record's roles and permissions are revoked.
|
167
|
+
|
168
|
+
As a final step, the record is 'given' (e.g. 'conjur resource give') to a destination role.
|
169
|
+
The default role to receive the record is the user 'attic'. This option can be used to specify
|
170
|
+
an alternative destination role.)
|
171
|
+
command.flag [:d, :"destination-role"]
|
172
|
+
end
|
173
|
+
|
174
|
+
def destination_role options
|
175
|
+
destination = options[:"destination-role"]
|
176
|
+
if destination
|
177
|
+
api.role(destination)
|
178
|
+
else
|
179
|
+
api.user('attic')
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def validate_retire_privileges record, options
|
184
|
+
if record.respond_to?(:role)
|
185
|
+
memberships = current_user.role.memberships.map(&:roleid)
|
186
|
+
validate_privileges "You can't administer this record" do
|
187
|
+
# The current user has a role which is admin of the record's role
|
188
|
+
record.role.members.find{|m| memberships.member?(m.member.roleid) && m.admin_option}
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
validate_privileges "You don't own the record" do
|
193
|
+
# The current user has the role which owns the record's resource
|
194
|
+
current_user.role.member_of?(record.resource.ownerid)
|
195
|
+
end
|
196
|
+
|
197
|
+
role = destination_role(options)
|
198
|
+
exit_now! "Destination role '#{role.roleid}' doesn't exist" unless role.exists?
|
199
|
+
end
|
200
|
+
|
103
201
|
def retire_resource obj
|
104
202
|
obj.resource.attributes['permissions'].each do |p|
|
105
203
|
role = api.role(p['role'])
|
@@ -111,13 +209,36 @@ module Conjur
|
|
111
209
|
end
|
112
210
|
|
113
211
|
def retire_role obj
|
114
|
-
obj.role.members
|
212
|
+
members = obj.role.members
|
213
|
+
# Move the invoking role to the end of the roles list, so that it doesn't
|
214
|
+
# lose its permissions in the middle of this operation.
|
215
|
+
# I'm sure there's a cleaner way to do this.
|
216
|
+
self_member = members.select{|m| m.member.roleid == current_user.role.roleid}
|
217
|
+
self_member.each do |m|
|
218
|
+
members.delete m
|
219
|
+
end
|
220
|
+
members.concat self_member if self_member
|
221
|
+
members.each do |r|
|
115
222
|
member = api.role(r.member)
|
116
223
|
puts "Revoking from role #{member.roleid}"
|
117
224
|
obj.role.revoke_from member
|
118
225
|
end
|
119
226
|
end
|
120
227
|
|
228
|
+
def give_away_resource obj, options
|
229
|
+
destination = options[:"destination-role"]
|
230
|
+
destination_role = if destination
|
231
|
+
api.role(destination)
|
232
|
+
else
|
233
|
+
api.user('attic')
|
234
|
+
end
|
235
|
+
|
236
|
+
exit_now! "Role #{destination_role.roleid} doesn't exist" unless destination_role.exists?
|
237
|
+
|
238
|
+
puts "Giving ownership to '#{destination_role.roleid}'"
|
239
|
+
obj.resource.give_to destination_role
|
240
|
+
end
|
241
|
+
|
121
242
|
def display_members(members, options)
|
122
243
|
result = if options[:V]
|
123
244
|
members.collect {|member|
|
@@ -148,6 +269,106 @@ module Conjur
|
|
148
269
|
puts str
|
149
270
|
end
|
150
271
|
|
272
|
+
def prompt_to_confirm kind, properties
|
273
|
+
puts
|
274
|
+
puts "A new #{kind} will be created with the following properties:"
|
275
|
+
puts
|
276
|
+
properties.select{|k,v| !v.blank?}.each do |k,v|
|
277
|
+
printf "%-10s: %s\n", k, v
|
278
|
+
end
|
279
|
+
puts
|
280
|
+
|
281
|
+
exit(0) unless %w(yes y).member?(highline.ask("Proceed? (yes/no): ").strip.downcase)
|
282
|
+
end
|
283
|
+
|
284
|
+
def integer? v
|
285
|
+
Integer(v, 10) rescue false
|
286
|
+
end
|
287
|
+
|
288
|
+
def prompt_for_id kind, label = 'id'
|
289
|
+
highline.ask("Enter the #{label}: ") do |q|
|
290
|
+
q.readline = true
|
291
|
+
q.validate = lambda{|id|
|
292
|
+
!id.blank? && !api.send(kind, id).exists?
|
293
|
+
}
|
294
|
+
q.responses[:not_valid] = "<% if @answer.blank? %>"\
|
295
|
+
"#{label} cannot be blank<% else %>"\
|
296
|
+
"A #{kind} called '<%= @answer %>' already exists<% end %>"
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def prompt_for_public_key
|
301
|
+
public_key = highline.ask("Enter the public key (press enter to skip): ") do |q|
|
302
|
+
q.validate = lambda{|key|
|
303
|
+
if key.blank?
|
304
|
+
true
|
305
|
+
else
|
306
|
+
validate_public_key key
|
307
|
+
end
|
308
|
+
}
|
309
|
+
q.responses[:not_valid] = "Public key format is invalid; please try again"
|
310
|
+
end
|
311
|
+
public_key.blank? ? nil : public_key.strip
|
312
|
+
end
|
313
|
+
|
314
|
+
# http://serverfault.com/questions/453296/how-do-i-validate-a-rsa-ssh-public-key-file-id-rsa-pub
|
315
|
+
def validate_public_key key
|
316
|
+
if system('which ssh-keygen 2>&1 > /dev/null')
|
317
|
+
Conjur.log.debug "Using ssh-keygen to verify the public key\n" if Conjur.log
|
318
|
+
require 'tempfile'
|
319
|
+
tempfile = Tempfile.new 'public_key'
|
320
|
+
tempfile.write(key)
|
321
|
+
tempfile.close
|
322
|
+
`ssh-keygen -l -f #{tempfile.path}`
|
323
|
+
$? == 0
|
324
|
+
else
|
325
|
+
Conjur.log.debug "ssh-keygen is not available; falling back to simple string testing\n" if Conjur.log
|
326
|
+
# Should be a line with at least 2 components,
|
327
|
+
# first one being the algo id and second a base64 string.
|
328
|
+
# In principle this means:
|
329
|
+
# Base64.strict_decode64 key.strip[/\Assh-\w+ (\S+).*/, 1]
|
330
|
+
|
331
|
+
# Since the pubkeys service is more strict: needs a name and
|
332
|
+
# rejects ones with a space, instead reproduce its algorithm here.
|
333
|
+
begin
|
334
|
+
components = key.strip.split ' '
|
335
|
+
Base64.strict_decode64 components[1]
|
336
|
+
components.length == 3
|
337
|
+
rescue NoMethodError, ArgumentError
|
338
|
+
false
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def prompt_for_group options = {}
|
344
|
+
options[:hint] ||= "press enter to own the record yourself"
|
345
|
+
group_ids = api.groups.map(&:id)
|
346
|
+
|
347
|
+
highline.ask("Enter the group which will own the record (#{options[:hint]}): ", [ "" ] + group_ids) do |q|
|
348
|
+
require 'readline'
|
349
|
+
Readline.completion_append_character = ""
|
350
|
+
Readline.completer_word_break_characters = ""
|
351
|
+
|
352
|
+
q.readline = true
|
353
|
+
q.validate = lambda{|id|
|
354
|
+
@group = nil
|
355
|
+
id.empty? || (@group = api.group(id)).exists?
|
356
|
+
}
|
357
|
+
q.responses[:not_valid] = "Group '<%= @answer %>' doesn't exist, or you don't have permission to use it"
|
358
|
+
end
|
359
|
+
@group ? @group.roleid : nil
|
360
|
+
end
|
361
|
+
|
362
|
+
def prompt_for_idnumber label
|
363
|
+
result = highline.ask("Enter a #{label}: ") do |q|
|
364
|
+
q.validate = lambda{|id|
|
365
|
+
id.blank? || integer?(id)
|
366
|
+
}
|
367
|
+
q.responses[:not_valid] = "The #{label} must be an integer"
|
368
|
+
end
|
369
|
+
result.blank? ? nil : result.to_i
|
370
|
+
end
|
371
|
+
|
151
372
|
def prompt_for_password
|
152
373
|
require 'highline'
|
153
374
|
# use stderr to allow output redirection, e.g.
|
@@ -155,6 +376,14 @@ module Conjur
|
|
155
376
|
hl = HighLine.new($stdin, $stderr)
|
156
377
|
|
157
378
|
password = hl.ask("Enter the password (it will not be echoed): "){ |q| q.echo = false }
|
379
|
+
if password.blank?
|
380
|
+
if hl.agree "No password (y/n)?"
|
381
|
+
return nil
|
382
|
+
else
|
383
|
+
return prompt_for_password
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
158
387
|
confirmation = hl.ask("Confirm the password: "){ |q| q.echo = false }
|
159
388
|
|
160
389
|
raise "Password does not match confirmation" unless password == confirmation
|
@@ -21,12 +21,61 @@
|
|
21
21
|
|
22
22
|
class Conjur::Command::Bootstrap < Conjur::Command
|
23
23
|
desc "Create initial users, groups, and permissions"
|
24
|
+
long_desc %Q(When you launch a new Conjur master server, it contains only one login: the "admin" user.
|
25
|
+
The bootstrap command will finish the setup of a new Conjur system by creating other essential records.
|
26
|
+
|
27
|
+
Actions performed by "bootstrap" include:
|
28
|
+
|
29
|
+
* Creation of a group called "security_admin".
|
30
|
+
|
31
|
+
* Giving the "security_admin" the power to manage public keys.
|
32
|
+
|
33
|
+
* Creation of a user called "attic", which will be the owner of retired records.
|
34
|
+
|
35
|
+
* Storing the "attic" user's API key in a variable called "conjur/users/attic/api-key".
|
36
|
+
|
37
|
+
* (optional) Create a new user who will be made a member and admin of the "security_admin" group.
|
38
|
+
|
39
|
+
* (optional) If a new user was created, login as that user.
|
40
|
+
)
|
41
|
+
|
42
|
+
# Determines whether the current logged-in user is sufficiently powerful to perform bootstrap.
|
43
|
+
# This is currently determined by detecting whether the logged-in role:
|
44
|
+
#
|
45
|
+
# * Is a user
|
46
|
+
# * Has admin privilege on the security_admin group role
|
47
|
+
# * Is an owner of the security_admin group resource
|
48
|
+
#
|
49
|
+
# The admin user will always satisfy these conditions, unless they are revoked for some reason.
|
50
|
+
# Other users created by the bootstrap command will (typically) also have these powers.
|
51
|
+
def self.security_admin_manager? api
|
52
|
+
username = api.username
|
53
|
+
user = if username.index('/')
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
api.user(username)
|
57
|
+
end
|
58
|
+
security_admin = api.group("security_admin")
|
59
|
+
memberships = user.role.memberships.map(&:roleid) if user
|
60
|
+
begin
|
61
|
+
# The user exists
|
62
|
+
# The security_admin group exists
|
63
|
+
# The user has a role which is admin of the security_admin role
|
64
|
+
# The user has the role which owns the security_admin resource
|
65
|
+
user &&
|
66
|
+
security_admin.exists? &&
|
67
|
+
security_admin.role.members.find{|m| memberships.member?(m.member.roleid) && m.admin_option} &&
|
68
|
+
memberships.member?(security_admin.resource.ownerid)
|
69
|
+
rescue RestClient::Forbidden
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
24
73
|
|
25
74
|
Conjur::CLI.command :bootstrap do |c|
|
26
75
|
c.action do |global_options,options,args|
|
27
76
|
require 'highline/import'
|
28
77
|
|
29
|
-
exit_now! "You must be
|
78
|
+
exit_now! "You must be an administrator to bootstrap Conjur" unless security_admin_manager?(api)
|
30
79
|
|
31
80
|
if (security_admin = api.group("security_admin")).exists?
|
32
81
|
puts "Group 'security_admin' exists"
|
@@ -35,16 +84,22 @@ class Conjur::Command::Bootstrap < Conjur::Command
|
|
35
84
|
security_admin = api.create_group("security_admin")
|
36
85
|
end
|
37
86
|
|
38
|
-
|
39
|
-
|
87
|
+
security_admin.resource.give_to(security_admin) unless security_admin.resource.ownerid == security_admin.role.roleid
|
88
|
+
|
89
|
+
key_managers = api.group("pubkeys-1.0/key-managers")
|
90
|
+
unless security_admin.role.memberships.map(&:roleid).member?(key_managers.role.roleid)
|
91
|
+
puts "Permitting group 'security_admin' to manage public keys"
|
92
|
+
key_managers.add_member security_admin, admin_option: true
|
93
|
+
end
|
40
94
|
|
41
95
|
security_administrators = security_admin.role.members.select{|m| m.member.roleid.split(':')[1..-1] != [ 'user', 'admin'] }
|
42
96
|
puts "Current 'security_admin' members are : #{security_administrators.map{|m| m.member.roleid.split(':')[-1]}.join(', ')}" unless security_administrators.blank?
|
97
|
+
created_user = nil
|
43
98
|
if security_administrators.empty? || agree("Create a new security_admin? (answer 'y' or 'yes'):")
|
44
99
|
username = ask("Enter #{security_administrators.empty? ? 'your' : 'the'} username:")
|
45
100
|
password = prompt_for_password
|
46
101
|
puts "Creating user '#{username}'"
|
47
|
-
user = api.create_user(username, password: password)
|
102
|
+
created_user = user = api.create_user(username, password: password)
|
48
103
|
Conjur::API.new_from_key(user.login, password).user(user.login).resource.give_to security_admin
|
49
104
|
puts "User created"
|
50
105
|
puts "Making '#{username}' a member and admin of group 'security_admin'"
|
@@ -53,11 +108,22 @@ class Conjur::Command::Bootstrap < Conjur::Command
|
|
53
108
|
puts "Adminship granted"
|
54
109
|
end
|
55
110
|
|
56
|
-
|
57
|
-
|
111
|
+
attic_user_name = "attic"
|
112
|
+
if (attic = api.user(attic_user_name)).exists?
|
113
|
+
puts "User '#{attic_user_name}' already exists"
|
58
114
|
else
|
59
|
-
puts "Creating user '
|
60
|
-
attic = api.create_user(
|
115
|
+
puts "Creating user '#{attic_user_name}' to own retired records"
|
116
|
+
attic = api.create_user(attic_user_name)
|
117
|
+
api.create_variable "text/plain",
|
118
|
+
"conjur-api-key",
|
119
|
+
id: "conjur/users/#{attic_user_name}/api-key",
|
120
|
+
value: attic.api_key,
|
121
|
+
ownerid: security_admin.role.roleid
|
122
|
+
end
|
123
|
+
|
124
|
+
if created_user && agree("Login as user '#{created_user.login}'? (answer 'y' or 'yes'):")
|
125
|
+
Conjur::Authn.fetch_credentials(username: created_user.login, password: created_user.api_key)
|
126
|
+
puts "Logged in as '#{created_user.login}'"
|
61
127
|
end
|
62
128
|
end
|
63
129
|
end
|
@@ -37,12 +37,34 @@ class Conjur::Command::Groups < Conjur::Command
|
|
37
37
|
|
38
38
|
acting_as_option(c)
|
39
39
|
|
40
|
-
c
|
41
|
-
id = require_arg(args, 'id')
|
40
|
+
interactive_option c
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
c.action do |global_options,options,args|
|
43
|
+
id = args.shift
|
44
|
+
|
45
|
+
interactive = options[:interactive] || id.blank?
|
46
|
+
|
47
|
+
groupid = options[:ownerid]
|
48
|
+
gidnumber = options[:gidnumber]
|
49
|
+
|
50
|
+
if interactive
|
51
|
+
id ||= prompt_for_id :group
|
52
|
+
|
53
|
+
groupid ||= prompt_for_group
|
54
|
+
gidnumber ||= prompt_for_gidnumber
|
55
|
+
|
56
|
+
prompt_to_confirm :group, {
|
57
|
+
"Id" => id,
|
58
|
+
"Owner" => groupid,
|
59
|
+
"Gidnumber" => gidnumber
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
group_options = { }
|
64
|
+
group_options[:ownerid] = groupid if groupid
|
65
|
+
group_options[:gidnumber] = gidnumber.to_i unless gidnumber.blank?
|
66
|
+
|
67
|
+
group = api.create_group(id, group_options)
|
46
68
|
display(group, options)
|
47
69
|
end
|
48
70
|
end
|
@@ -92,16 +114,18 @@ class Conjur::Command::Groups < Conjur::Command
|
|
92
114
|
group.desc "Decommission a group"
|
93
115
|
group.arg_name "id"
|
94
116
|
group.command :retire do |c|
|
117
|
+
retire_options c
|
118
|
+
|
95
119
|
c.action do |global_options,options,args|
|
96
120
|
id = require_arg(args, 'id')
|
97
121
|
|
98
122
|
group = api.group(id)
|
99
123
|
|
124
|
+
validate_retire_privileges group, options
|
125
|
+
|
100
126
|
retire_resource group
|
101
127
|
retire_role group
|
102
|
-
|
103
|
-
puts "Giving ownership to 'attic'"
|
104
|
-
group.resource.give_to api.user('attic')
|
128
|
+
give_away_resource group, options
|
105
129
|
|
106
130
|
puts "Group retired"
|
107
131
|
end
|
@@ -168,7 +192,9 @@ class Conjur::Command::Groups < Conjur::Command
|
|
168
192
|
end
|
169
193
|
|
170
194
|
end
|
171
|
-
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.prompt_for_gidnumber
|
198
|
+
prompt_for_idnumber "gid number"
|
172
199
|
end
|
173
200
|
end
|
174
|
-
|
data/lib/conjur/command/hosts.rb
CHANGED
@@ -58,10 +58,14 @@ class Conjur::Command::Hosts < Conjur::Command
|
|
58
58
|
hosts.desc "Decommission a host"
|
59
59
|
hosts.arg_name "id"
|
60
60
|
hosts.command :retire do |c|
|
61
|
+
retire_options c
|
62
|
+
|
61
63
|
c.action do |global_options,options,args|
|
62
64
|
id = require_arg(args, 'id')
|
63
65
|
|
64
66
|
host = api.host(id)
|
67
|
+
|
68
|
+
validate_retire_privileges host, options
|
65
69
|
|
66
70
|
host_layer_roles(host).each do |layer|
|
67
71
|
puts "Removing from layer #{layer.id}"
|
@@ -70,9 +74,7 @@ class Conjur::Command::Hosts < Conjur::Command
|
|
70
74
|
|
71
75
|
retire_resource host
|
72
76
|
retire_role host
|
73
|
-
|
74
|
-
puts "Giving ownership to 'attic'"
|
75
|
-
host.resource.give_to api.user('attic')
|
77
|
+
give_away_resource host, options
|
76
78
|
|
77
79
|
puts "Host retired"
|
78
80
|
end
|
@@ -47,17 +47,39 @@ class Conjur::Command::Pubkeys < Conjur::Command
|
|
47
47
|
end
|
48
48
|
|
49
49
|
pubkeys.desc "Add a public key for a user"
|
50
|
-
pubkeys.
|
50
|
+
pubkeys.long_desc %Q(Adds a public key for a user. The username is a required argument of this method.
|
51
|
+
|
52
|
+
The public key itself may be provided in several ways.
|
53
|
+
|
54
|
+
1. After the username argument, the public key can be provided as a literal (quoted) string.
|
55
|
+
|
56
|
+
2. After the username argument, the path to the public key file can be provided with a leading @ character.
|
57
|
+
|
58
|
+
3. If the only argument to this command is the username, the key will be read from stdin.
|
59
|
+
|
60
|
+
4. If you provide the -i (interactive) command option, you'll be prompted for the public key
|
61
|
+
)
|
62
|
+
pubkeys.arg_name "username key?"
|
51
63
|
pubkeys.command :add do |c|
|
64
|
+
interactive_option c
|
65
|
+
|
52
66
|
c.action do |global_options, options, args|
|
67
|
+
options[:interactive] = $stdin.isatty if options[:interactive].nil?
|
53
68
|
username = require_arg args, "username"
|
54
69
|
if key = args.shift
|
55
70
|
if /^@(.+)$/ =~ key
|
56
71
|
key = File.read(File.expand_path($1))
|
57
72
|
end
|
58
73
|
else
|
59
|
-
key =
|
74
|
+
key = if options[:interactive]
|
75
|
+
prompt_for_public_key
|
76
|
+
else
|
77
|
+
STDIN.read.strip.tap do |k|
|
78
|
+
exit_now! "Invalid public key format" unless validate_public_key(k)
|
79
|
+
end
|
80
|
+
end
|
60
81
|
end
|
82
|
+
fail "Cancelled by the user" if key.blank?
|
61
83
|
api.add_public_key username, key
|
62
84
|
puts "Public key '#{key.split(' ').last}' added"
|
63
85
|
end
|
@@ -74,4 +96,4 @@ class Conjur::Command::Pubkeys < Conjur::Command
|
|
74
96
|
end
|
75
97
|
end
|
76
98
|
end
|
77
|
-
end
|
99
|
+
end
|
@@ -138,12 +138,22 @@ class Conjur::Command::Resources < Conjur::Command
|
|
138
138
|
resource.desc "Set an annotation on a resource"
|
139
139
|
resource.arg_name "resource-id name value"
|
140
140
|
resource.command :annotate do |c|
|
141
|
+
interactive_option c
|
142
|
+
|
141
143
|
c.action do |global_options, options, args|
|
142
144
|
id = full_resource_id require_arg(args, 'resource-id')
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
145
|
+
|
146
|
+
annotations = if options[:interactive]
|
147
|
+
prompt_for_annotations
|
148
|
+
else
|
149
|
+
name = require_arg args, 'name'
|
150
|
+
value = require_arg args, 'value'
|
151
|
+
{ name => value }
|
152
|
+
end
|
153
|
+
unless annotations.blank?
|
154
|
+
api.resource(id).annotations.merge!(annotations)
|
155
|
+
puts "Set annotations #{annotations.keys} for resource '#{id}'"
|
156
|
+
end
|
147
157
|
end
|
148
158
|
end
|
149
159
|
|
data/lib/conjur/command/users.rb
CHANGED
@@ -35,20 +35,54 @@ class Conjur::Command::Users < Conjur::Command
|
|
35
35
|
|
36
36
|
acting_as_option(c)
|
37
37
|
|
38
|
-
c
|
39
|
-
login = require_arg(args, 'login')
|
38
|
+
interactive_option c
|
40
39
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
c.action do |global_options,options,args|
|
41
|
+
login = args.shift
|
42
|
+
|
43
|
+
interactive = options[:interactive] || login.blank?
|
44
|
+
|
45
|
+
groupid = options[:ownerid]
|
46
|
+
uidnumber = options[:uidnumber]
|
47
|
+
password = nil
|
48
|
+
exit_now! "uidnumber should be integer" unless uidnumber.blank? || /\d+/ =~ uidnumber
|
49
|
+
|
50
|
+
if interactive
|
51
|
+
login ||= prompt_for_id :user, "login name"
|
52
|
+
|
53
|
+
groupid ||= prompt_for_group hint: "press enter to have the user own their own record"
|
54
|
+
uidnumber ||= prompt_for_uidnumber
|
55
|
+
password = prompt_for_password unless options[:"no-password"]
|
56
|
+
|
57
|
+
attributes = {
|
58
|
+
"Login" => login,
|
59
|
+
"Owner" => groupid,
|
60
|
+
"UID Number" => uidnumber
|
61
|
+
}
|
62
|
+
attributes["Password"] = "********" unless password.blank?
|
63
|
+
prompt_to_confirm :user, attributes
|
45
64
|
end
|
46
|
-
|
47
|
-
if options[:p]
|
48
|
-
|
65
|
+
|
66
|
+
if options[:p] && password.blank?
|
67
|
+
password = prompt_for_password
|
49
68
|
end
|
50
69
|
|
51
|
-
|
70
|
+
user_options = { }
|
71
|
+
user_options[:ownerid] = groupid if groupid
|
72
|
+
user_options[:uidnumber] = uidnumber.to_i if uidnumber
|
73
|
+
user_options[:password] = password if password
|
74
|
+
user = api.create_user(login, user_options)
|
75
|
+
|
76
|
+
puts "User created"
|
77
|
+
display user
|
78
|
+
|
79
|
+
if interactive
|
80
|
+
public_key = prompt_for_public_key
|
81
|
+
if public_key
|
82
|
+
api.add_public_key user.login, public_key
|
83
|
+
puts "Public key added"
|
84
|
+
end
|
85
|
+
end
|
52
86
|
end
|
53
87
|
end
|
54
88
|
|
@@ -64,16 +98,18 @@ class Conjur::Command::Users < Conjur::Command
|
|
64
98
|
user.desc "Decommission a user"
|
65
99
|
user.arg_name "id"
|
66
100
|
user.command :retire do |c|
|
101
|
+
retire_options c
|
102
|
+
|
67
103
|
c.action do |global_options,options,args|
|
68
104
|
id = require_arg(args, 'id')
|
69
105
|
|
70
106
|
user = api.user(id)
|
71
107
|
|
108
|
+
validate_retire_privileges user, options
|
109
|
+
|
72
110
|
retire_resource user
|
73
111
|
retire_role user
|
74
|
-
|
75
|
-
puts "Giving ownership to 'attic'"
|
76
|
-
user.resource.give_to api.user('attic')
|
112
|
+
give_away_resource user, options
|
77
113
|
|
78
114
|
puts "User retired"
|
79
115
|
end
|
@@ -125,7 +161,9 @@ class Conjur::Command::Users < Conjur::Command
|
|
125
161
|
display api.find_users(uidnumber: uidnumber)
|
126
162
|
end
|
127
163
|
end
|
128
|
-
|
129
164
|
end
|
130
|
-
|
165
|
+
|
166
|
+
def self.prompt_for_uidnumber
|
167
|
+
prompt_for_idnumber "uid number"
|
168
|
+
end
|
131
169
|
end
|
@@ -34,55 +34,57 @@ class Conjur::Command::Variables < Conjur::Command
|
|
34
34
|
c.desc "Initial value, which may also be specified as the second command argument after the variable id"
|
35
35
|
c.flag [:v, :"value"]
|
36
36
|
|
37
|
-
acting_as_option
|
38
|
-
|
39
|
-
c
|
40
|
-
c.desc 'Create variable interactively'
|
41
|
-
c.switch [:i, :'interactive']
|
37
|
+
acting_as_option c
|
38
|
+
|
39
|
+
annotate_option c
|
42
40
|
|
41
|
+
interactive_option c
|
42
|
+
|
43
43
|
c.action do |global_options,options, args|
|
44
44
|
@default_mime_type = c.flags[:m].default_value
|
45
45
|
@default_kind = c.flags[:k].default_value
|
46
46
|
|
47
47
|
id = args.shift unless args.empty?
|
48
|
-
|
49
48
|
value = args.shift unless args.empty?
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
groupid = options[:
|
54
|
-
mime_type = options
|
55
|
-
kind = options
|
56
|
-
value ||= options
|
57
|
-
|
58
|
-
options
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
50
|
+
exit_now! "Received conflicting value arguments" if value && options[:value]
|
51
|
+
|
52
|
+
groupid = options[:ownerid]
|
53
|
+
mime_type = options[:m]
|
54
|
+
kind = options[:k]
|
55
|
+
value ||= options[:v]
|
56
|
+
interactive = options[:interactive] || id.blank?
|
57
|
+
annotate = options[:annotate]
|
58
|
+
|
59
|
+
exit_now! "Received --annotate option without --interactive" if annotate && !interactive
|
60
|
+
|
63
61
|
annotations = {}
|
64
|
-
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
62
|
+
# If the user asked for interactive mode, or he didn't specify and id
|
63
|
+
# prompt for any missing options.
|
64
|
+
if interactive
|
65
|
+
id ||= prompt_for_id :variable
|
66
|
+
|
70
67
|
groupid ||= prompt_for_group
|
71
68
|
|
72
69
|
kind = prompt_for_kind if !kind || kind == @default_kind
|
70
|
+
|
71
|
+
mime_type = prompt_for_mime_type if mime_type.blank? || mime_type == @default_mime_type
|
73
72
|
|
74
|
-
|
75
|
-
|
76
|
-
annotations = prompt_for_annotations
|
73
|
+
annotations = prompt_for_annotations if annotate
|
77
74
|
|
78
75
|
value ||= prompt_for_value
|
76
|
+
|
77
|
+
prompt_to_confirm :variable, "Id" => id,
|
78
|
+
"Kind" => kind,
|
79
|
+
"MIME type" => mime_type,
|
80
|
+
"Owner" => groupid,
|
81
|
+
"Value" => value
|
79
82
|
end
|
80
83
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
var = api.create_variable(mime_type, kind, options)
|
84
|
+
variable_options = { id: id }
|
85
|
+
variable_options[:ownerid] = groupid if groupid
|
86
|
+
variable_options[:value] = value unless value.blank?
|
87
|
+
var = api.create_variable(mime_type, kind, variable_options)
|
86
88
|
api.resource(var).annotations.merge!(annotations) if annotations && !annotations.empty?
|
87
89
|
display(var, options)
|
88
90
|
end
|
@@ -100,15 +102,17 @@ class Conjur::Command::Variables < Conjur::Command
|
|
100
102
|
var.desc "Decommission a variable"
|
101
103
|
var.arg_name "id"
|
102
104
|
var.command :retire do |c|
|
105
|
+
retire_options c
|
106
|
+
|
103
107
|
c.action do |global_options,options,args|
|
104
108
|
id = require_arg(args, 'id')
|
105
109
|
|
106
110
|
variable = api.variable(id)
|
111
|
+
|
112
|
+
validate_retire_privileges variable, options
|
107
113
|
|
108
114
|
retire_resource variable
|
109
|
-
|
110
|
-
puts "Giving ownership to 'attic'"
|
111
|
-
variable.resource.give_to api.user('attic')
|
115
|
+
give_away_resource variable, options
|
112
116
|
|
113
117
|
puts "Variable retired"
|
114
118
|
end
|
@@ -149,58 +153,24 @@ class Conjur::Command::Variables < Conjur::Command
|
|
149
153
|
$stdout.write api.variable(id).value(options[:version])
|
150
154
|
end
|
151
155
|
end
|
152
|
-
|
153
156
|
end
|
154
|
-
|
155
|
-
def self.prompt_for_id
|
156
|
-
highline.ask('Enter the id: ')
|
157
|
-
end
|
158
|
-
|
159
|
-
def self.prompt_for_group
|
160
|
-
highline.ask('Enter the group: ', ->(name) { @group && @group.roleid } ) do |q|
|
161
|
-
q.validate = ->(name) do
|
162
|
-
name.empty? || (@group = api.group(name)).exists?
|
163
|
-
end
|
164
|
-
q.responses[:not_valid] = "Group '<%= @answer %>' doesn't exist, or you don't have permission to use it"
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
157
|
+
|
168
158
|
def self.prompt_for_kind
|
169
159
|
highline.ask('Enter the kind: ') {|q| q.default = @default_kind }
|
170
160
|
end
|
171
161
|
|
172
162
|
def self.prompt_for_mime_type
|
173
|
-
highline.
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
until (name = highline.ask('annotation name: ')).empty?
|
180
|
-
annotations[name] = read_till_eof('annotation value (^D to finish):')
|
163
|
+
highline.choose do |menu|
|
164
|
+
menu.prompt = 'Enter the MIME type: '
|
165
|
+
menu.choice @default_mime_type
|
166
|
+
menu.choices *%w(application/json application/xml application/x-yaml application/x-pem-file)
|
167
|
+
menu.choice "other", nil do |c|
|
168
|
+
@highline.ask('Enter a custom mime type: ')
|
181
169
|
end
|
182
170
|
end
|
183
171
|
end
|
184
172
|
|
185
173
|
def self.prompt_for_value
|
186
|
-
read_till_eof('Enter the value (^D on its own line to finish):')
|
187
|
-
end
|
188
|
-
|
189
|
-
def self.highline
|
190
|
-
require 'highline'
|
191
|
-
@highline ||= HighLine.new($stdin,$stderr)
|
192
|
-
end
|
193
|
-
|
194
|
-
def self.read_till_eof(prompt = nil)
|
195
|
-
highline.say(prompt) if prompt
|
196
|
-
[].tap do |lines|
|
197
|
-
loop do
|
198
|
-
begin
|
199
|
-
lines << highline.ask('')
|
200
|
-
rescue EOFError
|
201
|
-
break
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end.join("\n")
|
174
|
+
read_till_eof('Enter the secret value (^D on its own line to finish):')
|
205
175
|
end
|
206
176
|
end
|
data/lib/conjur/version.rb
CHANGED
@@ -61,6 +61,8 @@ describe Conjur::Command::Pubkeys, logged_in: true do
|
|
61
61
|
let(:stdin_contents){ "ssh-rsa blahblah keyname" }
|
62
62
|
it "calls api.add_public_key('alice', stdin) and prints the key name" do
|
63
63
|
expect(STDIN).to receive(:read).and_return(stdin_contents)
|
64
|
+
allow(STDIN).to receive(:isatty).and_return(false)
|
65
|
+
expect(described_class).to receive(:validate_public_key).and_return(true)
|
64
66
|
expect(described_class.api).to receive(:add_public_key).with('alice', stdin_contents)
|
65
67
|
expect{ invoke }.to write("Public key 'keyname' added")
|
66
68
|
end
|
@@ -149,4 +149,14 @@ describe Conjur::Command::Resources, logged_in: true do
|
|
149
149
|
expect(JSON.parse( expect { invoke }.to write )).to eq(roles_list)
|
150
150
|
end
|
151
151
|
end
|
152
|
+
|
153
|
+
context "interactivity" do
|
154
|
+
subject { Conjur::Command::Resources }
|
155
|
+
describe_command 'resource:annotate -i #{KIND}:#{ID}' do
|
156
|
+
it {
|
157
|
+
is_expected.to receive(:prompt_for_annotations)
|
158
|
+
invoke_silently
|
159
|
+
}
|
160
|
+
end
|
161
|
+
end
|
152
162
|
end
|
@@ -12,10 +12,10 @@ describe Conjur::Command::Variables, logged_in: true do
|
|
12
12
|
end
|
13
13
|
let(:id) { 'the-id' }
|
14
14
|
let(:variable) { post_response(id) }
|
15
|
-
let
|
16
|
-
let
|
17
|
-
let
|
18
|
-
let
|
15
|
+
let(:group) { nil }
|
16
|
+
let(:annotation) { {} }
|
17
|
+
let(:value) { 'the-value' }
|
18
|
+
let(:full_payload) { base_payload }
|
19
19
|
|
20
20
|
context 'when there are command-line errors' do
|
21
21
|
describe_command "variable:create -v the-value-1 the-id the-value-2" do
|
@@ -25,16 +25,34 @@ describe Conjur::Command::Variables, logged_in: true do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
context "-a without -i" do
|
29
|
+
describe_command 'variable:create -a the-id' do
|
30
|
+
it "is an error" do
|
31
|
+
expect { invoke }.to raise_error("Received --annotate option without --interactive")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'non-interactive' do
|
37
|
+
describe_command "variable:create the-id" do
|
38
|
+
it "is non-interactive" do
|
39
|
+
allow(Conjur::Command::Variables).to receive(:prompt_for_id).and_raise("Unexpected prompt for id")
|
40
|
+
expect(RestClient::Request).to receive(:execute).and_return(variable)
|
41
|
+
invoke
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
28
46
|
context 'when there are no command-line errors' do
|
29
|
-
|
30
47
|
before do
|
48
|
+
allow(Conjur::Command::Variables).to receive(:prompt_to_confirm) { "yes"}
|
31
49
|
allow(Conjur::Command::Variables).to receive(:prompt_for_id) { id }
|
32
50
|
allow(Conjur::Command::Variables).to receive(:prompt_for_group) { group }
|
33
51
|
allow(Conjur::Command::Variables).to receive(:prompt_for_kind) { kind }
|
34
52
|
allow(Conjur::Command::Variables).to receive(:prompt_for_mime_type) { mime_type }
|
35
53
|
allow(Conjur::Command::Variables).to receive(:prompt_for_annotations) { annotation }
|
36
54
|
allow(Conjur::Command::Variables).to receive(:prompt_for_value) { value }
|
37
|
-
|
55
|
+
|
38
56
|
expect(RestClient::Request).to receive(:execute).with({
|
39
57
|
method: :post,
|
40
58
|
url: collection_url,
|
@@ -43,29 +61,42 @@ describe Conjur::Command::Variables, logged_in: true do
|
|
43
61
|
}.merge(cert_store_options)).and_return(variable)
|
44
62
|
end
|
45
63
|
|
46
|
-
describe_command "variable:create the-different-
|
47
|
-
let (:
|
64
|
+
describe_command "variable:create the-id the-different-value" do
|
65
|
+
let (:value) { 'the-different-value' }
|
48
66
|
it "propagates the user-assigned id" do
|
49
|
-
expect { invoke }.to write({ id: 'the-
|
67
|
+
expect { invoke }.to write({ id: 'the-id' }).to(:stdout)
|
50
68
|
end
|
51
69
|
end
|
52
70
|
|
53
|
-
describe_command "variable:create the-id
|
54
|
-
let
|
55
|
-
|
71
|
+
describe_command "variable:create the-id" do
|
72
|
+
let(:value) { "" }
|
73
|
+
let(:full_payload) {
|
74
|
+
base_payload.dup.tap do |m|
|
75
|
+
m.delete_if{|k,_| k == :value}
|
76
|
+
end
|
77
|
+
}
|
78
|
+
it "will propagate the user-assigned id without a value" do
|
56
79
|
expect { invoke }.to write({ id: 'the-id' }).to(:stdout)
|
57
80
|
end
|
58
81
|
end
|
59
82
|
|
83
|
+
let(:base_payload) do
|
84
|
+
{ id: id, value: value, mime_type: mime_type, kind: kind }.tap do |t|
|
85
|
+
group && t.merge(ownerid: group)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
60
89
|
describe_command "variable:create -m application/json" do
|
61
|
-
let
|
90
|
+
let(:mime_type) { 'application/json' }
|
91
|
+
let(:payload) { valueless_payload }
|
62
92
|
it "propagates the user-assigned MIME type" do
|
63
93
|
expect { invoke }.to write({ id: 'the-id' }).to(:stdout)
|
64
94
|
end
|
65
95
|
end
|
66
96
|
|
67
97
|
describe_command "variable:create -k password" do
|
68
|
-
let
|
98
|
+
let(:kind) { 'password' }
|
99
|
+
let(:payload) { valueless_payload }
|
69
100
|
it "propagates the user-assigned kind" do
|
70
101
|
expect { invoke }.to write({ id: 'the-id' }).to(:stdout)
|
71
102
|
end
|
@@ -84,25 +115,22 @@ describe Conjur::Command::Variables, logged_in: true do
|
|
84
115
|
it { is_expected.to receive(:prompt_for_group) }
|
85
116
|
it { is_expected.to receive(:prompt_for_kind) }
|
86
117
|
it { is_expected.to receive(:prompt_for_mime_type) }
|
87
|
-
it { is_expected.
|
118
|
+
it { is_expected.not_to receive(:prompt_for_annotations) }
|
88
119
|
it { is_expected.to receive(:prompt_for_value) }
|
89
120
|
end
|
90
121
|
|
91
|
-
describe_command 'variable:create the-id' do
|
92
|
-
it { is_expected.not_to receive(:prompt_for_id) }
|
93
|
-
end
|
94
|
-
|
95
122
|
describe_command 'variable:create the-id the-value' do
|
123
|
+
it { is_expected.not_to receive(:prompt_for_id) }
|
96
124
|
it { is_expected.not_to receive(:prompt_for_value) }
|
97
125
|
end
|
98
126
|
|
99
127
|
describe_command 'variable:create -m application/json' do
|
100
|
-
let
|
128
|
+
let(:mime_type) { 'application/json' }
|
101
129
|
it { is_expected.not_to receive(:prompt_for_mime_type) }
|
102
130
|
end
|
103
131
|
|
104
132
|
describe_command 'variable:create -k password' do
|
105
|
-
let
|
133
|
+
let(:kind) { 'password' }
|
106
134
|
it { is_expected.not_to receive(:prompt_for_kind) }
|
107
135
|
end
|
108
136
|
|
@@ -119,7 +147,7 @@ describe Conjur::Command::Variables, logged_in: true do
|
|
119
147
|
}.merge(cert_store_options)).and_return(OpenStruct.new(headers: {}, body: '{}'))
|
120
148
|
end
|
121
149
|
|
122
|
-
let
|
150
|
+
let(:full_payload) { base_payload.merge(ownerid: 'the-account:group:the-group') }
|
123
151
|
|
124
152
|
it { is_expected.not_to receive(:prompt_for_group) }
|
125
153
|
end
|
@@ -133,25 +161,32 @@ describe Conjur::Command::Variables, logged_in: true do
|
|
133
161
|
}.merge(cert_store_options)).and_return(OpenStruct.new(headers: {}, body: '{}'))
|
134
162
|
end
|
135
163
|
|
136
|
-
let
|
164
|
+
let(:full_payload) { base_payload.merge(ownerid: 'the-account:group:the-group') }
|
137
165
|
|
138
166
|
it { is_expected.not_to receive(:prompt_for_group) }
|
139
167
|
end
|
140
168
|
|
141
169
|
end
|
142
170
|
|
143
|
-
context "
|
171
|
+
context "explicit interactivity" do
|
144
172
|
describe_command 'variable:create -i the-id the-value' do
|
145
173
|
it { is_expected.not_to receive(:prompt_for_id) }
|
146
174
|
it { is_expected.not_to receive(:prompt_for_value) }
|
147
175
|
it { is_expected.to receive(:prompt_for_group) }
|
148
176
|
it { is_expected.to receive(:prompt_for_mime_type) }
|
149
177
|
it { is_expected.to receive(:prompt_for_kind) }
|
150
|
-
it { is_expected.
|
178
|
+
it { is_expected.not_to receive(:prompt_for_annotations) }
|
151
179
|
end
|
152
180
|
end
|
153
181
|
|
182
|
+
context "interactive annotations" do
|
183
|
+
describe_command 'variable:create -a' do
|
184
|
+
it { is_expected.to receive(:prompt_for_annotations) }
|
185
|
+
end
|
186
|
+
describe_command 'variable:create -ia the-id' do
|
187
|
+
it { is_expected.to receive(:prompt_for_annotations) }
|
188
|
+
end
|
189
|
+
end
|
154
190
|
end
|
155
|
-
|
156
191
|
end
|
157
192
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: conjur-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.25.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rafal Rzepecki
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-05-
|
12
|
+
date: 2015-05-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -269,6 +269,7 @@ files:
|
|
269
269
|
- CHANGELOG.md
|
270
270
|
- Gemfile
|
271
271
|
- LICENSE
|
272
|
+
- PUBLISH.md
|
272
273
|
- README.md
|
273
274
|
- Rakefile
|
274
275
|
- bin/_conjur_completions
|