conjur-cli 5.5.0 → 5.6.3

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.
data/build-deb.sh CHANGED
@@ -3,7 +3,9 @@
3
3
  export DEBUG=true
4
4
  export GLI_DEBUG=true
5
5
 
6
- debify clean
6
+ if [[ "$(id -un)" == "jenkins" ]]; then
7
+ docker run -i --rm -v $PWD:/src -w /src alpine/git clean -fxd
8
+ fi
7
9
 
8
10
  docker build -t conjur-cli-fpm -f Dockerfile.fpm .
9
11
  docker build -t conjur-cli-validate-packaging -f Dockerfile.validate-packaging .
@@ -1,2 +1,2 @@
1
- ART_USERNAME: !var artifactory/users/jenkins/username
2
- ART_PASSWORD: !var artifactory/users/jenkins/password
1
+ ART_USERNAME: !var ci/artifactory/users/jenkins/username
2
+ ART_PASSWORD: !var ci/artifactory/users/jenkins/password
@@ -15,8 +15,8 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Conjur::VERSION
17
17
 
18
- gem.add_dependency 'activesupport', '>= 4.2'
19
- gem.add_dependency 'conjur-api', '>= 4.28.1'
18
+ gem.add_dependency 'activesupport', '~> 4.2'
19
+ gem.add_dependency 'conjur-api', '~> 4.30'
20
20
  gem.add_dependency 'gli', '>=2.8.0'
21
21
  gem.add_dependency 'highline', '~> 1.7'
22
22
  gem.add_dependency 'netrc', '~> 0.10'
@@ -24,6 +24,7 @@ Gem::Specification.new do |gem|
24
24
  gem.add_dependency 'deep_merge', '~> 1.0'
25
25
  gem.add_dependency 'xdg', '~> 2.2'
26
26
  gem.add_dependency 'table_print', '~> 1.5'
27
+ gem.add_dependency 'semantic', '>= 1.4.1'
27
28
 
28
29
  gem.add_runtime_dependency 'cas_rest_client', '~> 1.3'
29
30
 
@@ -142,35 +142,60 @@ module Conjur
142
142
  end
143
143
  end.join("\n")
144
144
  end
145
-
146
- def command_options_for_list(c)
145
+
146
+ def command_option_kind c
147
+ c.desc "Filter by kind"
148
+ c.flag [:k, :kind]
149
+ end
150
+
151
+ def command_option_as_role c
147
152
  return if c.flags.member?(:role) # avoid duplicate flags
148
153
  c.desc "Role to act as. By default, the current logged-in role is used."
149
154
  c.arg_name 'ROLE'
150
155
  c.flag [:role]
151
-
156
+ end
157
+
158
+ def command_options_for_search c
152
159
  c.desc "Full-text search on resource id and annotation values"
153
160
  c.flag [:s, :search]
154
161
 
155
- c.desc "Maximum number of records to return"
156
- c.flag [:l, :limit]
157
-
158
162
  c.desc "Offset to start from"
159
163
  c.flag [:o, :offset]
160
164
 
165
+ c.desc "Maximum number of records to return"
166
+ c.flag [:l, :limit]
167
+
168
+ c.desc "Show the number of results, rather than printing them"
169
+ c.switch [:count]
170
+ end
171
+
172
+ def command_options_for_list(c)
173
+ command_option_as_role c
174
+ command_options_for_search c
175
+
161
176
  c.desc "Show only ids"
162
177
  c.switch [:i, :ids]
163
-
178
+
164
179
  c.desc "Show annotations in 'raw' format"
165
180
  c.switch [:r, :"raw-annotations"]
166
181
  end
182
+
183
+ def process_command_options_for_search options
184
+ opts = options.slice(:search, :count, :limit, :offset, :kind)
185
+ opts[:acting_as] = options[:role] if options[:role]
186
+ opts[:search] = opts[:search].gsub('-',' ') if opts[:search]
187
+ if opts[:kind] && opts[:kind].index(',')
188
+ opts[:kind] = opts[:kind].split(',')
189
+ end
190
+ opts
191
+ end
167
192
 
168
193
  def command_impl_for_list(global_options, options, args)
169
- opts = options.slice(:search, :limit, :options, :kind)
170
- opts[:acting_as] = options[:role] if options[:role]
171
- opts[:search]=opts[:search].gsub('-',' ') if opts[:search]
194
+ opts = process_command_options_for_search(options)
172
195
  resources = api.resources(opts)
173
- if options[:ids]
196
+ if resources.is_a?(Numeric)
197
+ puts resources
198
+ elsif options[:ids]
174
199
  puts JSON.pretty_generate(resources.map(&:resourceid))
175
200
  else
176
201
  resources = resources.map &:attributes
@@ -297,23 +322,58 @@ an alternative destination role.)
297
322
  obj.resource.give_to destination_role
298
323
  end
299
324
 
300
- def display_members(members, options)
301
- result = if options[:V]
302
- members.collect {|member|
303
- {
304
- member: member.member.roleid,
305
- grantor: member.grantor.roleid,
306
- admin_option: member.admin_option
307
- }
308
- }
325
+ # Displays members, which may be either:
326
+ #
327
+ # * A numeric count
328
+ # * A list of role ids
329
+ # * A list of RoleGrant
330
+ #
331
+ # +options+ may include:
332
+ #
333
+ # * **V** Show full RoleGrant info
334
+ # * **system** show system (internal) roles
335
+ #
336
+ # @return [Numeric, Hash] Convert the input to a number or Hash.
337
+ def display_members(members, member_field, options)
338
+ # Get this case out of the way
339
+ return display(members) if members.is_a?(Numeric)
340
+
341
+ result, roleid_function = if members.blank?
342
+ [ [], nil ]
343
+ elsif members.first.is_a?(Conjur::RoleGrant)
344
+ if options[:V]
345
+ items = members.collect do |member|
346
+ {
347
+ member: member.member.roleid,
348
+ grantor: member.grantor.roleid,
349
+ admin_option: member.admin_option
350
+ }.tap do |obj|
351
+ obj[:role] = member.role.roleid if member.role
352
+ end
353
+ end
354
+ [
355
+ items, lambda {|member| member[member_field] }
356
+ ]
357
+ else
358
+ [ members.map{|m| m.send(member_field) }.map(&:roleid), lambda{|r| r} ]
359
+ end
309
360
  else
310
- members.map(&:member).map(&:roleid)
361
+ [ members.map(&:roleid), lambda{|r| r} ]
311
362
  end
363
+
364
+ unless options[:system]
365
+ result.reject! do |member|
366
+ roleid_function.call(member) =~ /^.+?:@/
367
+ end
368
+ end
369
+
312
370
  display result
313
371
  end
314
372
 
315
373
  def display(obj, options = {})
316
- str = if obj.respond_to?(:attributes)
374
+ str = if obj.is_a?(Numeric)
375
+ obj.to_s
376
+ elsif obj.respond_to?(:attributes)
317
377
  JSON.pretty_generate obj.attributes
318
378
  elsif obj.respond_to?(:id)
319
379
  obj.id
@@ -145,7 +145,7 @@ class Conjur::Command::Groups < Conjur::Command
145
145
  c.switch [:V,:verbose]
146
146
  c.action do |global_options,options,args|
147
147
  group = require_arg(args, 'GROUP')
148
- display_members api.group(group).role.members, options
148
+ display_members api.group(group).role.members, :member, options
149
149
  end
150
150
  end
151
151
 
@@ -137,10 +137,15 @@ class Conjur::Command::Resources < Conjur::Command
137
137
  resource.desc "List roles with a specified permission on the resource"
138
138
  resource.arg_name "RESOURCE PERMISSION"
139
139
  resource.command :permitted_roles do |c|
140
+ command_option_kind c
141
+ command_options_for_search c
142
+
140
143
  c.action do |global_options,options,args|
141
144
  id = full_resource_id( require_arg(args, "RESOURCE") )
142
145
  permission = require_arg(args, "PERMISSION")
143
- display api.resource(id).permitted_roles(permission)
146
+
147
+ opts = process_command_options_for_search(options)
148
+ display api.resource(id).permitted_roles(permission, opts)
144
149
  end
145
150
  end
146
151
 
@@ -191,9 +196,7 @@ class Conjur::Command::Resources < Conjur::Command
191
196
 
192
197
  resource.desc "List all resources"
193
198
  resource.command :list do |c|
194
- c.desc "Filter by kind"
195
- c.flag [:k, :kind]
196
-
199
+ command_option_kind c
197
200
  command_options_for_list c
198
201
 
199
202
  c.action do |global_options, options, args|
@@ -74,21 +74,32 @@ class Conjur::Command::Roles < Conjur::Command
74
74
  end
75
75
  end
76
76
 
77
- role.desc "Lists role memberships. The role membership list is recursively expanded."
77
+ role.desc "Lists role memberships. The role membership list is recursively expanded by default."
78
78
  role.arg_name "ROLE"
79
79
 
80
80
  role.command :memberships do |c|
81
+ c.desc "Verbose output. Only meaningful with --no-recursive."
82
+ c.switch [:V,:verbose]
83
+
84
+ c.desc "Whether to recursively expand role memberships"
85
+ c.default_value true
86
+ c.switch [:r, :recursive]
87
+
81
88
  c.desc "Whether to show system (internal) roles"
82
- c.switch [:s, :system]
89
+ c.switch [:system]
90
+
91
+ command_option_kind c
92
+ command_options_for_search c
83
93
 
84
94
  c.action do |global_options,options,args|
85
95
  roleid = args.shift
96
+ assert_empty(args)
86
97
  role = roleid.nil? && api.current_role || api.role(roleid)
87
- memberships = role.all.map(&:roleid)
88
- unless options[:system]
89
- memberships.reject!{|id| id =~ /^.+?:@/}
90
- end
91
- display memberships
98
+
99
+ opts = process_command_options_for_search(options)
100
+ opts[:recursive] = false unless options[:recursive]
101
+ memberships = role.all(opts)
102
+ display_members memberships, :role, options
92
103
  end
93
104
  end
94
105
 
@@ -98,9 +109,20 @@ class Conjur::Command::Roles < Conjur::Command
98
109
  c.desc "Verbose output"
99
110
  c.switch [:V,:verbose]
100
111
 
112
+ c.desc "Whether to show system (internal) roles"
113
+ c.switch [:system]
114
+
115
+ command_option_kind c
116
+ command_options_for_search c
117
+
101
118
  c.action do |global_options,options,args|
102
- role = args.shift || api.user(api.username).roleid
103
- display_members api.role(role).members, options
119
+ roleid = args.shift
120
+ assert_empty(args)
121
+ role = roleid.nil? && api.current_role || api.role(roleid)
122
+ opts = process_command_options_for_search(options)
123
+
124
+ members = role.members(opts)
125
+ display_members members, :member, options
104
126
  end
105
127
  end
106
128
 
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2014-2016 Conjur Inc.
2
+ # Copyright (C) 2014-2017 Conjur Inc.
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
5
  # this software and associated documentation files (the "Software"), to deal in
@@ -19,6 +19,6 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  module Conjur
22
- VERSION = '5.5.0'
22
+ VERSION = '5.6.3'.freeze
23
23
  ::Version=VERSION
24
24
  end
@@ -0,0 +1,11 @@
1
+ #!/bin/bash -e
2
+
3
+ docker run -i --rm -v $PWD:/src -w /src alpine/git clean -fxd
4
+
5
+ docker pull registry.tld/conjurinc/publish-rubygem
6
+
7
+ summon --yaml "RUBYGEMS_API_KEY: !var rubygems/api-key" \
8
+ docker run --rm --env-file @SUMMONENVFILE -v "$(pwd)":/opt/src \
9
+ registry.tld/conjurinc/publish-rubygem conjur-cli
10
+
11
+ docker run -i --rm -v $PWD:/src -w /src alpine/git clean -fxd
@@ -7,6 +7,7 @@ describe Conjur::Command::Resources, logged_in: true do
7
7
  let (:resource_attributes) { { "some" => "attribute"} }
8
8
 
9
9
  before :each do
10
+ allow(api).to receive(:resource).and_call_original
10
11
  allow(api).to receive(:resource).with(full_resource_id).and_return(resource_instance)
11
12
  end
12
13
 
@@ -135,18 +136,99 @@ describe Conjur::Command::Resources, logged_in: true do
135
136
  it { expect { invoke }.to write "Ownership granted" }
136
137
  end
137
138
 
138
- describe_command "resource:permitted_roles #{KIND}:#{ID} #{PRIVILEGE}" do
139
- let(:roles_list) { %W[klaatu barada nikto] }
140
- before(:each) {
141
- allow(resource_instance).to receive(:permitted_roles).and_return(roles_list)
139
+ context "list" do
140
+ def make_resource(kind, identifier, attributes)
141
+ authz_host = "http://conjur/authz"
142
+ credentials = {}
143
+ id = "the-account:#{kind}:#{identifier}"
144
+ api.resource(id).tap do |resource|
145
+ resource.attributes = attributes.merge(resourceid: id)
146
+ end
147
+ end
148
+ let(:resources) {
149
+ [
150
+ make_resource("food", "bacon", {}),
151
+ make_resource("food", "eggs", {})
152
+ ]
142
153
  }
143
- it_behaves_like "it obtains resource by id"
144
- it "calls resource.permitted_roles(#{PRIVILEGE}" do
145
- expect(resource_instance).to receive(:permitted_roles)
146
- invoke_silently
154
+ let(:resource_ids) {
155
+ [
156
+ "the-account:food:bacon",
157
+ "the-account:food:eggs"
158
+ ]
159
+ }
160
+ describe_command "resource:list" do
161
+ it "displays JSONised list of resources" do
162
+ expect(api).to receive(:resources).with({}).and_return(resources)
163
+ expect(JSON.parse( expect { invoke }.to write )).to eq([
164
+ {"resourceid"=>"the-account:food:bacon", "annotations"=>{}},
165
+ {"resourceid"=>"the-account:food:eggs", "annotations"=>{}}
166
+ ])
167
+ end
168
+ end
169
+ describe_command "resource:list -i -k jobs" do
170
+ it "searches by resource kind" do
171
+ expect(api).to receive(:resources).with({kind: 'jobs'}).and_return(resources)
172
+ expect(JSON.parse( expect { invoke }.to write )).to eq(resource_ids)
173
+ end
174
+ end
175
+ describe_command "resource:list -i" do
176
+ it "displays resource ids" do
177
+ expect(api).to receive(:resources).with({}).and_return(resources)
178
+ expect(JSON.parse( expect { invoke }.to write )).to eq(resource_ids)
179
+ end
180
+ end
181
+ { search: "hamster", offset: 10, limit: 10 }.each do |k,v|
182
+ describe_command "resource:list -i --#{k} #{v}" do
183
+ it "displays the items" do
184
+ expect(api).to receive(:resources).with({k => v.to_s}).and_return(resources)
185
+ expect(JSON.parse( expect { invoke }.to write )).to eq(resource_ids)
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ context "permitted roles" do
192
+ let(:roles_list) { %W[klaatu barada nikto] }
193
+ describe_command "resource:permitted_roles #{KIND}:#{ID} #{PRIVILEGE}" do
194
+ before(:each) {
195
+ allow(resource_instance).to receive(:permitted_roles).and_return(roles_list)
196
+ }
197
+ it_behaves_like "it obtains resource by id"
198
+ it "calls resource.permitted_roles(#{PRIVILEGE}" do
199
+ expect(resource_instance).to receive(:permitted_roles).with(PRIVILEGE, {})
200
+ invoke_silently
201
+ end
202
+ it "displays JSONised list of roles" do
203
+ expect(JSON.parse( expect { invoke }.to write )).to eq(roles_list)
204
+ end
205
+ end
206
+
207
+ describe_command "resource:permitted_roles --count #{KIND}:#{ID} #{PRIVILEGE}" do
208
+ before {
209
+ expect(resource_instance).to receive(:permitted_roles).with(PRIVILEGE, count: true).
210
+ and_return(12)
211
+ }
212
+ it_behaves_like "it obtains resource by id"
213
+ it "calls resource.permitted_roles(#{PRIVILEGE}" do
214
+ invoke_silently
215
+ end
216
+ it "displays role count" do
217
+ expect(JSON.parse( expect { invoke }.to write )).to eq(12)
218
+ end
147
219
  end
148
- it "displays JSONised list of roles" do
149
- expect(JSON.parse( expect { invoke }.to write )).to eq(roles_list)
220
+
221
+
222
+ describe_command "resource:permitted_roles -s frontend #{KIND}:#{ID} #{PRIVILEGE}" do
223
+ let(:roles_list) { %W[klaatu barada nikto] }
224
+ before {
225
+ expect(resource_instance).to receive(:permitted_roles).with(PRIVILEGE, search: "frontend").
226
+ and_return(roles_list)
227
+ }
228
+ it_behaves_like "it obtains resource by id"
229
+ it "displays JSONised list of roles" do
230
+ expect(JSON.parse( expect { invoke }.to write )).to eq(roles_list)
231
+ end
150
232
  end
151
233
  end
152
234
 
@@ -46,13 +46,92 @@ describe Conjur::Command::Roles, logged_in: true do
46
46
  end
47
47
  end
48
48
 
49
+ describe "role:members" do
50
+ let(:all_roles) { %w(foo:user:joerandom foo:something:cool foo:something:else foo:group:admins) }
51
+ let(:all_role_grants) {
52
+ all_roles.map do |r|
53
+ Conjur::RoleGrant.new(api.role("foo:user:joerandom"), api.role(r), api.role("foo:user:admin"), false)
54
+ end
55
+ }
56
+ let(:role) do
57
+ double "the role", members: all_role_grants
58
+ end
59
+
60
+ before do
61
+ allow(api).to receive(:role).and_call_original
62
+ allow(api).to receive(:role).with(rolename).and_return role
63
+ end
64
+
65
+ context "when logged in as a user" do
66
+ let(:username) { "joerandom" }
67
+ let(:rolename) { "user:joerandom" }
68
+
69
+ describe_command "role:members" do
70
+ it "lists all roles" do
71
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles)
72
+ end
73
+ end
74
+
75
+ describe_command "role:members -V" do
76
+ it "lists all roles verbosely" do
77
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_role_grants.map(&:to_h).map(&:stringify_keys))
78
+ end
79
+ describe "without RoleGrant.role field" do
80
+ it "lists the roles verbosely" do
81
+ all_role_grants.each do |rg|
82
+ rg.instance_variable_set "@role", nil
83
+ end
84
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_role_grants.map(&:to_h).map(&:stringify_keys))
85
+ end
86
+ end
87
+ end
88
+
89
+ describe_command "role:members --count" do
90
+ it "counts the roles" do
91
+ expect(role).to receive(:members).with({count: true}).and_return(all_roles.size)
92
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles.size)
93
+ end
94
+ end
95
+
96
+ describe_command "role:members -k hamster -s frontend -o 10 -l 10" do
97
+ it "lists selected roles" do
98
+ expect(role).to receive(:members).with({kind: 'hamster', search: 'frontend', offset: "10", limit: "10"}).and_return(all_role_grants)
99
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles)
100
+ end
101
+ end
102
+
103
+ describe_command "role:members -k hamster,giraffe" do
104
+ it "lists selected roles" do
105
+ expect(role).to receive(:members).with({kind: %w(hamster giraffe)}).and_return(all_role_grants)
106
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles)
107
+ end
108
+ end
109
+
110
+ describe_command "role:members -k hamster -k giraffe" do
111
+ it "applies only the last 'kind' filter" do
112
+ expect(role).to receive(:members).with({kind: 'giraffe'}).and_return(all_role_grants)
113
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles)
114
+ end
115
+ end
116
+
117
+ describe_command "role:members foo:bar" do
118
+ let(:rolename) { 'foo:bar' }
119
+ it "lists all roles of foo:bar" do
120
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles)
121
+ end
122
+ end
123
+ end
124
+ end
125
+
49
126
  describe "role:memberships" do
50
127
  let(:all_roles) { %w(foo:user:joerandom foo:something:cool foo:something:else foo:group:admins) }
128
+ let(:all_role_objects) { all_roles.map{|r| double r, roleid: r } }
51
129
  let(:role) do
52
- double "the role", all: all_roles.map{|r| double r, roleid: r }
130
+ double "the role", all: all_role_objects
53
131
  end
54
132
 
55
133
  before do
134
+ allow(api).to receive(:role).and_call_original
56
135
  allow(api).to receive(:role).with(rolename).and_return role
57
136
  end
58
137
 
@@ -65,6 +144,59 @@ describe Conjur::Command::Roles, logged_in: true do
65
144
  expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles)
66
145
  end
67
146
  end
147
+
148
+ describe_command "when empty" do
149
+ let(:all_roles) { [] }
150
+ describe_command "role:memberships" do
151
+ it "prints an empty array" do
152
+ expect(JSON::parse(expect { invoke }.to write)).to eq([])
153
+ end
154
+ end
155
+ end
156
+
157
+ describe_command "role:memberships" do
158
+ it "hides system roles" do
159
+ expect(role).to receive(:all).with({}).and_return([
160
+ double(:role, roleid: "the-account:@:hamster")
161
+ ])
162
+ expect(JSON::parse(expect { invoke }.to write)).to eq([])
163
+ end
164
+ end
165
+
166
+ describe_command "role:memberships --count" do
167
+ it "counts the roles" do
168
+ expect(role).to receive(:all).with({count: true}).and_return(all_roles.size)
169
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles.size)
170
+ end
171
+ end
172
+
173
+ context "with full role grant info" do
174
+ let(:all_role_grants) {
175
+ all_roles.map do |r|
176
+ Conjur::RoleGrant.new(api.role(r), api.role("foo:user:joerandom"), api.role("foo:user:admin"), false)
177
+ end
178
+ }
179
+ before {
180
+ expect(role).to receive(:all).with({recursive: false}).and_return(all_role_grants)
181
+ }
182
+ describe_command "role:memberships --no-recursive" do
183
+ it "lists the roles" do
184
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles)
185
+ end
186
+ end
187
+ describe_command "role:memberships -V --no-recursive" do
188
+ it "shows all the roles" do
189
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_role_grants.map(&:to_h).map(&:stringify_keys))
190
+ end
191
+ end
192
+ end
193
+
194
+ describe_command "role:memberships -k hamster -s frontend -o 10 -l 10" do
195
+ it "lists selected roles" do
196
+ expect(role).to receive(:all).with({kind: 'hamster', search: 'frontend', offset: "10", limit: "10"}).and_return(all_role_objects)
197
+ expect(JSON::parse(expect { invoke }.to write)).to eq(all_roles)
198
+ end
199
+ end
68
200
 
69
201
  describe_command "role:memberships foo:bar" do
70
202
  let(:rolename) { 'foo:bar' }