conjur-cli 4.25.2 → 4.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +1 -0
- data/README.md +6 -0
- data/Rakefile +1 -33
- data/bin/{_conjur_completions → _conjur} +5 -26
- data/lib/conjur/command.rb +7 -4
- data/lib/conjur/command/audit.rb +10 -0
- data/lib/conjur/command/env.rb +1 -0
- data/lib/conjur/command/groups.rb +14 -15
- data/lib/conjur/command/hosts.rb +9 -9
- data/lib/conjur/command/init.rb +2 -0
- data/lib/conjur/command/layers.rb +22 -22
- data/lib/conjur/command/plugin.rb +6 -6
- data/lib/conjur/command/policy.rb +2 -2
- data/lib/conjur/command/pubkeys.rb +8 -8
- data/lib/conjur/command/resources.rb +30 -30
- data/lib/conjur/command/roles.rb +14 -14
- data/lib/conjur/command/rspec/audit_helpers.rb +0 -1
- data/lib/conjur/command/script.rb +2 -2
- data/lib/conjur/command/shellinit.rb +36 -0
- data/lib/conjur/command/users.rb +8 -8
- data/lib/conjur/command/variables.rb +12 -12
- data/lib/conjur/complete.rb +263 -0
- data/lib/conjur/version.rb +1 -1
- data/spec/command/audit_spec.rb +19 -0
- data/spec/complete_spec.rb +265 -0
- data/spec/spec_helper.rb +1 -0
- metadata +8 -6
- data/bin/_conjur_completions.yaml +0 -106
@@ -22,15 +22,15 @@ class Conjur::Command::Variables < Conjur::Command
|
|
22
22
|
desc "Manage variables"
|
23
23
|
command :variable do |var|
|
24
24
|
var.desc "Create and store a variable"
|
25
|
-
var.arg_name "
|
25
|
+
var.arg_name "NAME VALUE"
|
26
26
|
var.command :create do |c|
|
27
|
-
c.arg_name "
|
27
|
+
c.arg_name "MIME-TYPE"
|
28
28
|
c.flag [:m, :"mime-type"], default_value: 'text/plain'
|
29
29
|
|
30
|
-
c.arg_name "
|
30
|
+
c.arg_name "KIND"
|
31
31
|
c.flag [:k, :"kind"], default_value: 'secret'
|
32
32
|
|
33
|
-
c.arg_name "
|
33
|
+
c.arg_name "VALUE"
|
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
|
|
@@ -91,21 +91,21 @@ class Conjur::Command::Variables < Conjur::Command
|
|
91
91
|
end
|
92
92
|
|
93
93
|
var.desc "Show a variable"
|
94
|
-
var.arg_name "
|
94
|
+
var.arg_name "VARIABLE"
|
95
95
|
var.command :show do |c|
|
96
96
|
c.action do |global_options,options,args|
|
97
|
-
id = require_arg(args, '
|
97
|
+
id = require_arg(args, 'VARIABLE')
|
98
98
|
display(api.variable(id), options)
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
102
|
var.desc "Decommission a variable"
|
103
|
-
var.arg_name "
|
103
|
+
var.arg_name "VARIABLE"
|
104
104
|
var.command :retire do |c|
|
105
105
|
retire_options c
|
106
106
|
|
107
107
|
c.action do |global_options,options,args|
|
108
|
-
id = require_arg(args, '
|
108
|
+
id = require_arg(args, 'VARIABLE')
|
109
109
|
|
110
110
|
variable = api.variable(id)
|
111
111
|
|
@@ -130,10 +130,10 @@ class Conjur::Command::Variables < Conjur::Command
|
|
130
130
|
var.desc "Access variable values"
|
131
131
|
var.command :values do |values|
|
132
132
|
values.desc "Add a value"
|
133
|
-
values.arg_name "
|
133
|
+
values.arg_name "VARIABLE VALUE"
|
134
134
|
values.command :add do |c|
|
135
135
|
c.action do |global_options,options,args|
|
136
|
-
id = require_arg(args, '
|
136
|
+
id = require_arg(args, 'VARIABLE')
|
137
137
|
value = args.shift || STDIN.read
|
138
138
|
|
139
139
|
api.variable(id).add_value(value)
|
@@ -143,13 +143,13 @@ class Conjur::Command::Variables < Conjur::Command
|
|
143
143
|
end
|
144
144
|
|
145
145
|
var.desc "Get a value"
|
146
|
-
var.arg_name "
|
146
|
+
var.arg_name "VARIABLE"
|
147
147
|
var.command :value do |c|
|
148
148
|
c.desc "Version number"
|
149
149
|
c.flag [:v, :version]
|
150
150
|
|
151
151
|
c.action do |global_options,options,args|
|
152
|
-
id = require_arg(args, '
|
152
|
+
id = require_arg(args, 'VARIABLE')
|
153
153
|
$stdout.write api.variable(id).value(options[:version])
|
154
154
|
end
|
155
155
|
end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2015 Conjur Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person
|
5
|
+
# obtaining a copy of this software and associated documentation files
|
6
|
+
# (the "Software"), to deal in the Software without restriction,
|
7
|
+
# including without limitation the rights to use, copy, modify, merge,
|
8
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
9
|
+
# and to permit persons to whom the Software is furnished to do so,
|
10
|
+
# subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
19
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
20
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
# SOFTWARE.
|
23
|
+
#
|
24
|
+
require 'conjur/cli'
|
25
|
+
require 'shellwords'
|
26
|
+
|
27
|
+
# Class for generating `conjur` bash completions
|
28
|
+
class Conjur::CLI::Complete
|
29
|
+
attr_reader :line, :words, :current_word_index, :commands,
|
30
|
+
:switch_words, :flag_words, :arg_words, :command_words
|
31
|
+
def initialize line, point=nil
|
32
|
+
@line = line
|
33
|
+
@words = tokenize_cmd @line
|
34
|
+
point ||= @line.length
|
35
|
+
@current_word_index=(tokenize_cmd @line.slice(0,point)).length-1
|
36
|
+
# fix arrays for empty "current word"
|
37
|
+
# ie "conjur group list "
|
38
|
+
if @line.match(/[ =]$/)
|
39
|
+
@words << ''
|
40
|
+
@current_word_index += 1
|
41
|
+
end
|
42
|
+
@commands,
|
43
|
+
@switch_words,
|
44
|
+
@flag_words,
|
45
|
+
@arg_words = parse_command @words, @current_word_index
|
46
|
+
@command_words = @commands
|
47
|
+
.drop(1)
|
48
|
+
.map(&:name)
|
49
|
+
.map(&:to_s)
|
50
|
+
.unshift('conjur')
|
51
|
+
end
|
52
|
+
|
53
|
+
def current_word offset=0
|
54
|
+
@words[@current_word_index + offset]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Generate array of subcommands for which documentation is not hidden
|
58
|
+
#
|
59
|
+
# @param cmd [Conjur::CLI::Command] the command to search
|
60
|
+
# @return [Array] the subcommands
|
61
|
+
def subcommands cmd
|
62
|
+
cmd.commands.select do |_, c|
|
63
|
+
c.nodoc.nil?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Generate array of symbols representing switches for +cmd+ and
|
68
|
+
# their aliases
|
69
|
+
#
|
70
|
+
# @param cmd [Conjur::CLI::Command] the command to search
|
71
|
+
# @return [Array] the symbols representing switches and their aliases
|
72
|
+
def switches cmd
|
73
|
+
cmd.switches.map { |_,switch|
|
74
|
+
[switch.name] + (switch.aliases or [])
|
75
|
+
}.flatten
|
76
|
+
end
|
77
|
+
|
78
|
+
# Split line according on spaces and after '='
|
79
|
+
#
|
80
|
+
# @param line [String] to split
|
81
|
+
# @return [Array] the substrings
|
82
|
+
def tokenize_cmd line
|
83
|
+
line.split(/ |(?<==)/)
|
84
|
+
end
|
85
|
+
|
86
|
+
def flag_to_sym flag
|
87
|
+
flag.match(/--?([^=]+)=?/)[1].to_sym
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_command words, current_word_index
|
91
|
+
command = Conjur::CLI
|
92
|
+
commands = [command]
|
93
|
+
switches = []
|
94
|
+
flags = []
|
95
|
+
arguments = []
|
96
|
+
index = 1
|
97
|
+
until index >= current_word_index do
|
98
|
+
word = words[index]
|
99
|
+
case classify_word word, command
|
100
|
+
when :switch
|
101
|
+
switches.push word
|
102
|
+
when :flag
|
103
|
+
flags.push [word, words[index+1]]
|
104
|
+
index += 1
|
105
|
+
when :subcommand
|
106
|
+
command = command.commands[word.to_sym]
|
107
|
+
commands.push command
|
108
|
+
when :argument
|
109
|
+
arguments.push word
|
110
|
+
end
|
111
|
+
index += 1
|
112
|
+
end
|
113
|
+
return commands, switches, flags, arguments
|
114
|
+
end
|
115
|
+
|
116
|
+
def classify_word word, command
|
117
|
+
if word.start_with? '-'
|
118
|
+
sym = flag_to_sym word
|
119
|
+
if switches(command).member? sym
|
120
|
+
:switch
|
121
|
+
else
|
122
|
+
:flag
|
123
|
+
end
|
124
|
+
else
|
125
|
+
if subcommands(command).has_key? word.to_sym
|
126
|
+
:subcommand
|
127
|
+
else
|
128
|
+
:argument
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def complete kind
|
134
|
+
kind = kind.to_s.downcase.gsub(/[^a-z]/, '')
|
135
|
+
case kind
|
136
|
+
when 'resource'
|
137
|
+
complete_resource
|
138
|
+
when 'role'
|
139
|
+
complete_role
|
140
|
+
when 'file'
|
141
|
+
complete_file current_word
|
142
|
+
when 'hostname'
|
143
|
+
complete_hostname
|
144
|
+
else
|
145
|
+
complete_resource kind if [
|
146
|
+
'group',
|
147
|
+
'user',
|
148
|
+
'variable',
|
149
|
+
'host',
|
150
|
+
'layer',
|
151
|
+
].member? kind
|
152
|
+
end or []
|
153
|
+
end
|
154
|
+
|
155
|
+
# generate completions for the switches and flags of a Conjur::CLI::Command
|
156
|
+
#
|
157
|
+
# @param cmd [Conjur::CLI::Command] command for which to search for flags
|
158
|
+
# and switches
|
159
|
+
# @return [Array] completion words
|
160
|
+
def complete_flags cmd
|
161
|
+
cmd.flags.values.map do |flag|
|
162
|
+
candidates = [flag.name]
|
163
|
+
candidates += flag.aliases if flag.aliases
|
164
|
+
candidates.map do |c|
|
165
|
+
"-#{'-' if c.length > 1}#{c}#{'=' if c.length > 1}"
|
166
|
+
end
|
167
|
+
end + cmd.switches.values.map do |switch|
|
168
|
+
candidates = [switch.name]
|
169
|
+
candidates += switch.aliases if switch.aliases
|
170
|
+
candidates.map do |c|
|
171
|
+
"-#{'-' if c.length > 1}#{c}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def complete_args cmd, prev, num_args
|
177
|
+
kind=nil
|
178
|
+
if prev.start_with? '-'
|
179
|
+
flag_name=flag_to_sym prev
|
180
|
+
flag = cmd.flags[flag_name]
|
181
|
+
desc = flag.argument_name if defined? flag.argument_name
|
182
|
+
kind = desc.to_s.downcase
|
183
|
+
else
|
184
|
+
desc = cmd.arguments_description if defined? cmd.arguments_description
|
185
|
+
kind = desc.to_s.downcase.split[num_args-1]
|
186
|
+
end
|
187
|
+
complete kind
|
188
|
+
end
|
189
|
+
|
190
|
+
def complete_resource resource_kind=nil
|
191
|
+
Conjur::Command.api.resources({kind: resource_kind})
|
192
|
+
.map do |r|
|
193
|
+
res = Resource.new r.attributes['id']
|
194
|
+
if resource_kind
|
195
|
+
res.name
|
196
|
+
else
|
197
|
+
res.to_s
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def complete_role
|
203
|
+
Conjur::Command.api.current_role.all
|
204
|
+
.map { |r| Resource.new(r.roleid) }
|
205
|
+
.reject { |r| r.kind.start_with? '@' }
|
206
|
+
.map(&:to_s)
|
207
|
+
end
|
208
|
+
|
209
|
+
def complete_file word
|
210
|
+
# use Bash's file completion for compatibility
|
211
|
+
`bash -c "compgen -f #{word}"`.shellsplit
|
212
|
+
end
|
213
|
+
|
214
|
+
def complete_hostname
|
215
|
+
`bash -c "compgen -A hostname"`.shellsplit
|
216
|
+
end
|
217
|
+
|
218
|
+
def completions
|
219
|
+
prev = current_word(-1)
|
220
|
+
if current_word.start_with? '-'
|
221
|
+
complete_flags @commands.last
|
222
|
+
else
|
223
|
+
(subcommands @commands.last).keys.map(&:to_s) +
|
224
|
+
(complete_args @commands.last, prev, @arg_words.length)
|
225
|
+
end.flatten
|
226
|
+
.select do |candidate|
|
227
|
+
candidate.start_with? current_word.sub('\:',':') end
|
228
|
+
.map do |candidate|
|
229
|
+
# if the current word is colon separated, strip its complete tokens
|
230
|
+
# eg. for --as-role=user:ryanprior, we're actually only completing 'ryanprior'
|
231
|
+
# because bash treats 'user' as a separate word
|
232
|
+
non_escaped_colon_regex = /(?<!\\):/
|
233
|
+
num_tokens = current_word.split(non_escaped_colon_regex).length
|
234
|
+
if num_tokens > 1
|
235
|
+
candidate = candidate
|
236
|
+
.split(non_escaped_colon_regex)
|
237
|
+
.drop(num_tokens-1)
|
238
|
+
.join(':')
|
239
|
+
end
|
240
|
+
"#{candidate}#{' ' if not candidate.end_with? '='}" end
|
241
|
+
end
|
242
|
+
public :current_word, :completions
|
243
|
+
end
|
244
|
+
|
245
|
+
class Conjur::CLI::Complete::Resource
|
246
|
+
attr_reader :account, :kind, :name
|
247
|
+
attr_accessor :include_account
|
248
|
+
def initialize resource_string, include_account=false
|
249
|
+
@include_account = include_account
|
250
|
+
fields = resource_string.split ':'
|
251
|
+
raise ArgumentError.new "too many fields (#{resource_string})" if fields.length > 3
|
252
|
+
fields.unshift nil while fields.length < 3
|
253
|
+
@account, @kind, @name = fields
|
254
|
+
end
|
255
|
+
|
256
|
+
def to_ary
|
257
|
+
[(@account if @include_account), @kind, @name].reject { |a| a.nil? }
|
258
|
+
end
|
259
|
+
|
260
|
+
def to_s
|
261
|
+
to_ary.join ':'
|
262
|
+
end
|
263
|
+
end
|
data/lib/conjur/version.rb
CHANGED
data/spec/command/audit_spec.rb
CHANGED
@@ -312,6 +312,25 @@ describe Conjur::Command::Audit, logged_in: true do
|
|
312
312
|
expect { invoke }.to write(" created role super:user")
|
313
313
|
end
|
314
314
|
end
|
315
|
+
|
316
|
+
describe 'audit of ssh:sudo' do
|
317
|
+
let(:ssh_event) { default_audit_event.merge('kind' => 'audit', 'facility' => 'ssh', 'action' => 'sudo', 'command' => '/bin/ls', 'system_user' => 'test_user', 'target_user' => 'root') }
|
318
|
+
context 'when sudo successful' do
|
319
|
+
let(:test_event) { ssh_event.merge('allowed' => true) }
|
320
|
+
it 'prints <user> ran <command>' do
|
321
|
+
expect { invoke }.to write(" test_user ran '/bin/ls' as root")
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context 'when sudo fails' do
|
326
|
+
let(:test_event) { ssh_event.merge('allowed' => false) }
|
327
|
+
|
328
|
+
it 'prints <user> attempted to run <command>' do
|
329
|
+
expect { invoke }.to write(" test_user attempted to run '/bin/ls' as root")
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
315
334
|
end
|
316
335
|
end
|
317
336
|
|
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Conjur::CLI::Complete do
|
4
|
+
def expects_completions_for string, point=nil
|
5
|
+
expect(described_class.new("conjur #{string}",point)
|
6
|
+
.completions
|
7
|
+
.map { |c| c.chomp ' ' })
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'conjur bash completion' do
|
11
|
+
describe 'for conjur subcommands beginning' do
|
12
|
+
|
13
|
+
before(:each) { expect(Conjur::Command).not_to receive :api }
|
14
|
+
|
15
|
+
context 'with "conjur gr"' do
|
16
|
+
it { expects_completions_for('gr').to include 'group' }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with "conjur group"' do
|
20
|
+
it { expects_completions_for('group').to contain_exactly 'group' }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'with "conjur p"' do
|
24
|
+
it { expects_completions_for('p').to include 'plugin',
|
25
|
+
'policy',
|
26
|
+
'pubkeys' }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with "conjur host l"' do
|
30
|
+
it { expects_completions_for('host l').to include 'layers',
|
31
|
+
'list' }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with "conjur policy"' do
|
35
|
+
it { expects_completions_for('policy ').to include 'load' }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'for deprecated subcommands such as `conjur field`' do
|
40
|
+
it { expects_completions_for('fi').not_to include 'field' }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'for command flags beginning' do
|
44
|
+
|
45
|
+
before(:each) { expect(Conjur::Command).not_to receive :api }
|
46
|
+
|
47
|
+
context 'conjur -' do
|
48
|
+
it { expects_completions_for('-').to include '--version' }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'conjur role memberships -' do
|
52
|
+
it { expects_completions_for('role memberships -')
|
53
|
+
.to include '-s', '--system'}
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'conjur audit all -' do
|
57
|
+
it { expects_completions_for('audit all -s -')
|
58
|
+
.to include '-f', '--follow', '-l', '--limit=',
|
59
|
+
'-o', '--offset=', '-s', '--short' }
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'conjur layer create --as-' do
|
63
|
+
it { expects_completions_for('layer create --as-')
|
64
|
+
.to include '--as-role=' }
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'conjur group create --as-role' do
|
68
|
+
it { expects_completions_for('layer create --as-role')
|
69
|
+
.to contain_exactly '--as-role=' }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'for arguments' do
|
74
|
+
|
75
|
+
let (:api) { double('api') }
|
76
|
+
before(:each) {
|
77
|
+
expect(Conjur::Command).to receive(:api).at_least(:once).and_return api
|
78
|
+
}
|
79
|
+
|
80
|
+
describe 'expecting a resource' do
|
81
|
+
|
82
|
+
let (:users) { ['Tweedledum', 'Tweedledee'] }
|
83
|
+
let (:groups) { ['sharks', 'jets'] }
|
84
|
+
let (:layers) { ['limbo', 'lust', 'gluttony', 'greed',
|
85
|
+
'anger', 'heresy', 'violence', 'fraud',
|
86
|
+
'treachery'] }
|
87
|
+
let (:variables) { ['id/superman', 'id/batman', 'id/spiderman'] }
|
88
|
+
let (:hosts) { ['skynet', 'hal9000', 'deep-thought'] }
|
89
|
+
|
90
|
+
|
91
|
+
def mock_resources
|
92
|
+
fake_results = yield.map { |result|
|
93
|
+
double('resource', :attributes => { 'id' => result })
|
94
|
+
}
|
95
|
+
expect(Conjur::Command.api).to receive(:resources)
|
96
|
+
.once.and_return fake_results
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'with kind "user"' do
|
100
|
+
before(:each) { mock_resources { users.map { |user| "user:#{user}" }}}
|
101
|
+
it { expects_completions_for('user show ')
|
102
|
+
.to contain_exactly(*users) }
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'with kind "group"' do
|
106
|
+
before(:each) {
|
107
|
+
mock_resources { groups.map { |group| "group:#{group}" }}
|
108
|
+
}
|
109
|
+
context 'for a command' do
|
110
|
+
it { expects_completions_for('group show ')
|
111
|
+
.to contain_exactly(*groups) }
|
112
|
+
end
|
113
|
+
context 'for a flag' do
|
114
|
+
it { expects_completions_for('group create --as-group=')
|
115
|
+
.to contain_exactly(*groups) }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'with kind "layer"' do
|
120
|
+
before(:each) {
|
121
|
+
mock_resources { layers.map { |layer| "layer:#{layer}" }}
|
122
|
+
}
|
123
|
+
it { expects_completions_for('layer show ')
|
124
|
+
.to contain_exactly(*layers) }
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'with kind "variable"' do
|
128
|
+
before(:each) {
|
129
|
+
mock_resources { variables.map { |variable| "variable:#{variable}" }}
|
130
|
+
}
|
131
|
+
it { expects_completions_for('variable show ')
|
132
|
+
.to contain_exactly(*variables) }
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'with kind "host"' do
|
136
|
+
before(:each) {
|
137
|
+
mock_resources { hosts.map { |host| "host:#{host}" }}
|
138
|
+
}
|
139
|
+
it { expects_completions_for('host show ')
|
140
|
+
.to contain_exactly(*hosts)
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'without kind specified' do
|
145
|
+
let (:resources) { (users+groups+layers+variables+hosts)
|
146
|
+
.map { |res| "arbitrarykind:#{res}" }}
|
147
|
+
before(:each) { mock_resources { resources }}
|
148
|
+
it 'should show all resources with their reported kinds' do
|
149
|
+
expects_completions_for('resource show ')
|
150
|
+
.to contain_exactly(*resources)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe 'expecting a role' do
|
156
|
+
let (:roles) { ['layer:population/tire',
|
157
|
+
'host:bubs-4k',
|
158
|
+
'user:strongbad',
|
159
|
+
'user:strongsad',
|
160
|
+
'user:strongmad']}
|
161
|
+
before(:each) {
|
162
|
+
role_doubles = roles.map { |role| double('role', :roleid => role) }
|
163
|
+
expect(api).to receive(:current_role).once
|
164
|
+
.and_return double('current_role', :all => role_doubles)
|
165
|
+
}
|
166
|
+
it { expects_completions_for('role memberships ')
|
167
|
+
.to contain_exactly(*roles) }
|
168
|
+
it 'completes colon separated values per-token' do
|
169
|
+
expects_completions_for('layer list --role=host:b')
|
170
|
+
.to contain_exactly 'bubs-4k'
|
171
|
+
end
|
172
|
+
it 'recognizes shell-escaped colons' do
|
173
|
+
expects_completions_for('role members layer\:pop')
|
174
|
+
.to contain_exactly 'layer:population/tire'
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe 'completes mid-line' do
|
180
|
+
it 'completes a subcommand not at the end of a line' do
|
181
|
+
expect(described_class.new('conjur gr create dwarves/7', 9).completions)
|
182
|
+
.to include 'group '
|
183
|
+
end
|
184
|
+
it 'tolerates garbage flags and arguments' do
|
185
|
+
expect(described_class.new('conjur omg --lol wat pu').completions)
|
186
|
+
.to include 'pubkeys '
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe Conjur::CLI::Complete::Resource do
|
193
|
+
describe 'splits resource ids' do
|
194
|
+
describe '#initialize(resource_string)' do
|
195
|
+
describe 'accepts long or brief ids' do
|
196
|
+
context 'gratuitous id (4+ tokens)' do
|
197
|
+
it 'raises an ArgumentError' do
|
198
|
+
expect {
|
199
|
+
described_class.new '1:2:3:4'
|
200
|
+
}.to raise_error ArgumentError
|
201
|
+
end
|
202
|
+
end
|
203
|
+
context 'long id (3 tokens)' do
|
204
|
+
it 'stores all 3 tokens' do
|
205
|
+
dummy_string = 'acct:kind:name'
|
206
|
+
dummy = described_class.new dummy_string
|
207
|
+
expect(dummy.account).to eq 'acct'
|
208
|
+
expect(dummy.kind).to eq 'kind'
|
209
|
+
expect(dummy.name).to eq 'name'
|
210
|
+
end
|
211
|
+
end
|
212
|
+
context 'brief id (2 tokens)' do
|
213
|
+
it 'stores tokens in kind and name' do
|
214
|
+
dummy = described_class.new 'kind:name'
|
215
|
+
expect(dummy.account).to eq nil
|
216
|
+
expect(dummy.kind).to eq 'kind'
|
217
|
+
expect(dummy.name).to eq 'name'
|
218
|
+
end
|
219
|
+
end
|
220
|
+
context 'tiny id (1 token)' do
|
221
|
+
it 'stores token in name' do
|
222
|
+
dummy = described_class.new 'name'
|
223
|
+
expect(dummy.account).to eq nil
|
224
|
+
expect(dummy.kind).to eq nil
|
225
|
+
expect(dummy.name).to eq 'name'
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
it 'hides the account by default' do
|
230
|
+
expect(described_class.new('a:b:c').include_account).to eq false
|
231
|
+
expect(described_class.new('a:b:c', true).include_account).to eq true
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe '#to_ary' do
|
236
|
+
context 'account not important' do
|
237
|
+
it 'hides account when converting to array' do
|
238
|
+
dummy = described_class.new 'a:b:c'
|
239
|
+
expect(dummy.to_ary).to eq ['b','c']
|
240
|
+
end
|
241
|
+
end
|
242
|
+
context 'account is important' do
|
243
|
+
it 'includes account when converting to array' do
|
244
|
+
dummy = described_class.new 'a:b:c', true
|
245
|
+
expect(dummy.to_ary).to eq ['a','b','c']
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe '#to_s' do
|
251
|
+
context 'account not important' do
|
252
|
+
it 'hides account when converting to string' do
|
253
|
+
dummy = described_class.new 'test:user:admin'
|
254
|
+
expect(dummy.to_s).to eq 'user:admin'
|
255
|
+
end
|
256
|
+
end
|
257
|
+
context 'account is important' do
|
258
|
+
it 'includes account when converting to string' do
|
259
|
+
dummy = described_class.new 'test:user:admin', true
|
260
|
+
expect(dummy.to_s).to eq 'test:user:admin'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|