conjur-cli 5.5.0 → 5.6.3

Sign up to get free protection for your applications and to get access to all the features.
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' }