conjur-cli 4.28.2 → 4.29.0

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +8 -0
  3. data/.gitignore +2 -0
  4. data/.overcommit.yml +10 -0
  5. data/.rubocop.yml +14 -0
  6. data/CHANGELOG.md +16 -0
  7. data/Dockerfile +10 -0
  8. data/Gemfile +2 -0
  9. data/Rakefile +1 -1
  10. data/acceptance-features/audit/audit_event_send.feature +46 -43
  11. data/acceptance-features/audit/send.feature +0 -19
  12. data/acceptance-features/authentication/login.feature +0 -2
  13. data/acceptance-features/authentication/logout.feature +0 -3
  14. data/acceptance-features/authorization/resource/check.feature +6 -4
  15. data/acceptance-features/authorization/resource/create.feature +4 -2
  16. data/acceptance-features/authorization/resource/exists.feature +8 -6
  17. data/acceptance-features/authorization/resource/give.feature +3 -1
  18. data/acceptance-features/authorization/resource/show.feature +3 -1
  19. data/acceptance-features/authorization/role/graph.feature +0 -1
  20. data/acceptance-features/conjurenv/check.feature +3 -10
  21. data/acceptance-features/conjurenv/run.feature +3 -3
  22. data/acceptance-features/conjurenv/template.feature +1 -1
  23. data/acceptance-features/directory/hostfactory/create.feature +13 -0
  24. data/acceptance-features/directory/hostfactory/tokens.feature +16 -0
  25. data/acceptance-features/directory/layer/retire.feature +43 -0
  26. data/acceptance-features/directory/user/update_password.feature +0 -1
  27. data/acceptance-features/directory/variable/value.feature +3 -2
  28. data/acceptance-features/dsl/policy_owner.feature +21 -7
  29. data/acceptance-features/dsl/resource_owner.feature +4 -4
  30. data/acceptance-features/pubkeys/add.feature +4 -2
  31. data/acceptance-features/pubkeys/names.feature +6 -3
  32. data/acceptance-features/pubkeys/show.feature +4 -2
  33. data/acceptance-features/step_definitions/{cli.rb → cli_steps.rb} +18 -4
  34. data/acceptance-features/step_definitions/user_steps.rb +13 -12
  35. data/acceptance-features/support/env.rb +0 -1
  36. data/acceptance-features/support/hooks.rb +11 -14
  37. data/acceptance-features/support/world.rb +16 -18
  38. data/build-deb.sh +19 -0
  39. data/ci/test.sh +19 -0
  40. data/conjur.gemspec +9 -12
  41. data/debify.sh +4 -0
  42. data/distrib/bin/_conjur +3 -0
  43. data/distrib/bin/conjur +3 -0
  44. data/distrib/bin/conjurize +3 -0
  45. data/distrib/bin/jsonfield +3 -0
  46. data/features/conjurize.feature +25 -25
  47. data/features/support/env.rb +5 -1
  48. data/features/support/hooks.rb +0 -1
  49. data/jenkins.sh +29 -1
  50. data/lib/conjur/cli.rb +27 -4
  51. data/lib/conjur/command.rb +36 -0
  52. data/lib/conjur/command/audit.rb +12 -0
  53. data/lib/conjur/command/bootstrap.rb +5 -9
  54. data/lib/conjur/command/host_factories.rb +187 -0
  55. data/lib/conjur/command/hosts.rb +82 -2
  56. data/lib/conjur/command/layers.rb +28 -0
  57. data/lib/conjur/command/resources.rb +1 -0
  58. data/lib/conjur/command/rspec/mock_services.rb +1 -1
  59. data/lib/conjur/command/server.rb +67 -0
  60. data/lib/conjur/command/users.rb +67 -12
  61. data/lib/conjur/command/variables.rb +101 -14
  62. data/lib/conjur/conjurize.rb +25 -69
  63. data/lib/conjur/conjurize/script.rb +133 -0
  64. data/lib/conjur/version.rb +1 -1
  65. data/publish.sh +6 -0
  66. data/spec/command/elevate_spec.rb +1 -1
  67. data/spec/command/host_factories_spec.rb +38 -0
  68. data/spec/command/hosts_spec.rb +86 -22
  69. data/spec/command/users_spec.rb +51 -3
  70. data/spec/command/variable_expiration_spec.rb +174 -0
  71. data/spec/command/variables_spec.rb +1 -1
  72. data/spec/conjurize_spec.rb +70 -0
  73. metadata +61 -64
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013 Conjur Inc
2
+ # Copyright (C) 2013-2015 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
@@ -32,16 +32,22 @@ class Conjur::Command::Hosts < Conjur::Command
32
32
  c.arg_name "password"
33
33
  c.flag [:p,:password]
34
34
 
35
+ c.desc "A comma-delimited list of CIDR addresses to restrict host to (optional)"
36
+ c.flag [:cidr]
37
+
35
38
  acting_as_option(c)
36
39
 
37
40
  c.action do |global_options,options,args|
38
41
  id = args.shift
39
- options[:id] = id if id
40
42
 
41
43
  unless id
42
44
  ActiveSupport::Deprecation.warn "id argument will be required in future releases"
43
45
  end
44
46
 
47
+ cidr = format_cidr(options.delete(:cidr))
48
+ options[:id] = id if id
49
+ options[:cidr] = cidr unless cidr.nil?
50
+
45
51
  display api.create_host(options), options
46
52
  end
47
53
  end
@@ -88,6 +94,65 @@ class Conjur::Command::Hosts < Conjur::Command
88
94
  end
89
95
  end
90
96
 
97
+ hosts.desc "Rotate a host's API key"
98
+ hosts.command :rotate_api_key do |c|
99
+ c.desc "Login of host whose API key we want to rotate (default: logged-in host)"
100
+ c.flag [:host, :h]
101
+ c.action do |_global, options, _args|
102
+ if options.include?(:host)
103
+ host = options[:host]
104
+
105
+ unless api.host(host).exists?
106
+ exit_now! "host '#{host}' not found"
107
+ end
108
+
109
+ # Prepend 'host/' if it wasn't passed in
110
+ unless is_host_login?(host)
111
+ host = 'host/' + host
112
+ end
113
+
114
+ # Make sure we're not trying to rotate our own key with the user flag.
115
+ if api.username == host
116
+ exit_now! 'To rotate your own API key, use this command without the --host flag'
117
+ end
118
+
119
+ puts api.user(host).rotate_api_key
120
+ else
121
+ username, password = Conjur::Authn.read_credentials
122
+ # Make sure the current identity is a host
123
+ unless is_host_login?(username)
124
+ exit_now! "'#{username}' is not a valid host login, specify a host with the --host flag"
125
+ end
126
+
127
+ new_api_key = Conjur::API.rotate_api_key username, password
128
+ # Show the new one before saving credentials so we don't lose it on failure.
129
+ puts new_api_key
130
+ Conjur::Authn.save_credentials username: username, password: new_api_key
131
+ end
132
+ end
133
+ end
134
+
135
+ hosts.desc "Update a hosts's attributes"
136
+ hosts.arg_name "HOST"
137
+ hosts.command :update do |c|
138
+ c.desc "A comma-delimited list of CIDR addresses to restrict host to (optional). Use 'all' to reset"
139
+ c.flag [:cidr]
140
+
141
+ c.action do |global_options, options, args|
142
+ id = require_arg(args, 'HOST')
143
+
144
+ host = api.host(id)
145
+
146
+ cidr = format_cidr(options[:cidr])
147
+
148
+ host_options = { }
149
+ host_options[:cidr] = cidr unless cidr.nil?
150
+
151
+ host.update(host_options)
152
+ puts "Host updated"
153
+ end
154
+ end
155
+
91
156
  hosts.desc "[Deprecated] Enroll a new host into conjur"
92
157
  hosts.arg_name "HOST"
93
158
  hosts.command :enroll do |c|
@@ -111,4 +176,19 @@ class Conjur::Command::Hosts < Conjur::Command
111
176
  end
112
177
  end
113
178
  end
179
+
180
+ def self.format_cidr(cidr)
181
+ case cidr
182
+ when 'all'
183
+ []
184
+ when nil
185
+ nil
186
+ else
187
+ cidr.split(',').each {|x| x.strip!}
188
+ end
189
+ end
190
+
191
+ def self.is_host_login?(username)
192
+ username.start_with?('host/')
193
+ end
114
194
  end
@@ -104,6 +104,34 @@ class Conjur::Command::Layers < Conjur::Command
104
104
  end
105
105
  end
106
106
 
107
+ layer.desc "Decommission a layer"
108
+ layer.arg_name "LAYER"
109
+ layer.command :retire do |c|
110
+ retire_options c
111
+
112
+ c.action do |global_options,options,args|
113
+ id = require_arg(args, 'LAYER')
114
+
115
+ layer = api.layer(id)
116
+
117
+ validate_retire_privileges layer, options
118
+
119
+ retire_resource layer
120
+ retire_role layer
121
+ # retire internal roles for observe, use_host, admin_host
122
+ account = Conjur::Core::API.conjur_account
123
+ ['observe', 'use_host', 'admin_host'].each do |priv|
124
+ role_name = ['layer', id, priv].join('/')
125
+ role_id = [ account, '@', role_name].join(':')
126
+ role_obj = api.role(role_id)
127
+ retire_internal_role role_obj
128
+ end
129
+ give_away_resource layer, options
130
+
131
+ puts "Layer retired"
132
+ end
133
+ end
134
+
107
135
  layer.desc "Operations on hosts"
108
136
  layer.command :hosts do |hosts|
109
137
  hosts.desc "Permit a privilege on hosts in the layer"
@@ -68,6 +68,7 @@ class Conjur::Command::Resources < Conjur::Command
68
68
  id = full_resource_id( require_arg(args, "RESOURCE") )
69
69
  role = require_arg(args, "ROLE")
70
70
  privilege = require_arg(args, "PRIVILEGE")
71
+ $stderr.print "Granting #{role} permission to #{privilege} #{id}... "
71
72
  unless options[:g]
72
73
  api.resource(id).permit privilege, role
73
74
  else
@@ -1,7 +1,7 @@
1
1
  shared_context "with fake endpoints and test config" do
2
2
  let(:authn_host) { 'https://authn.example.com' }
3
3
  let(:authz_host) { 'https://authz.example.com' }
4
- let(:core_host) { 'https://core.example.com' }
4
+ let(:core_host) { 'https://core.example.com/api' }
5
5
  before do
6
6
  allow(Conjur::Authn::API).to receive(:host) { authn_host }
7
7
  allow(Conjur::Authz::API).to receive(:host) { authz_host }
@@ -0,0 +1,67 @@
1
+ #
2
+ # Copyright (C) 2016 Conjur Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ # this software and associated documentation files (the "Software"), to deal in
6
+ # the Software without restriction, including without limitation the rights to
7
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ # the Software, and to permit persons to whom the Software is furnished to do so,
9
+ # subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+
21
+ class Conjur::Command::Server < Conjur::Command
22
+ desc 'Show Conjur client and server versions'
23
+ command :version do |v|
24
+ v.action do |*_|
25
+ puts "Conjur client version #{Conjur::VERSION}"
26
+ show_server_version
27
+ end
28
+ end
29
+
30
+ desc 'Server information'
31
+ command :server do |server|
32
+ server.desc 'Show service versions'
33
+ server.command :version do |c|
34
+ c.action do |*_|
35
+ show_server_version
36
+ end
37
+ end
38
+
39
+ server.desc 'Show general server information'
40
+ server.command :info do |c|
41
+ c.action do |*_|
42
+ display Conjur::API.appliance_info
43
+ end
44
+ end
45
+
46
+ server.desc 'Show server health information'
47
+ server.command :health do |c|
48
+ c.desc 'Show health information for a remote host, from the perspective of this server'
49
+ c.flag :h, :host
50
+ c.action do |_, options, _|
51
+ display Conjur::API.appliance_health(options[:host])
52
+ end
53
+ end
54
+ end
55
+
56
+ class << self
57
+ def show_server_version
58
+ services = Conjur::API.appliance_info['services']
59
+ appliance = services.delete 'appliance'
60
+ puts "Conjur appliance version: #{appliance['version']}"
61
+ puts 'Conjur service versions:'
62
+ services.each do |name,info|
63
+ puts " #{name}: #{info['version']}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013 Conjur Inc
2
+ # Copyright (C) 2013-2015 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
@@ -33,6 +33,9 @@ class Conjur::Command::Users < Conjur::Command
33
33
  c.desc "UID number to be associated with user (optional)"
34
34
  c.flag [:uidnumber]
35
35
 
36
+ c.desc "A comma-delimited list of CIDR addresses to restrict user to (optional)"
37
+ c.flag [:cidr]
38
+
36
39
  acting_as_option(c)
37
40
 
38
41
  interactive_option c
@@ -44,9 +47,11 @@ class Conjur::Command::Users < Conjur::Command
44
47
 
45
48
  groupid = options[:ownerid]
46
49
  uidnumber = options[:uidnumber]
50
+ cidr = format_cidr(options[:cidr])
47
51
  password = nil
48
- exit_now! "uidnumber should be integer" unless uidnumber.blank? || /\d+/ =~ uidnumber
49
-
52
+
53
+ validate_uidnumber(uidnumber)
54
+
50
55
  if interactive
51
56
  login ||= prompt_for_id :user, "login name"
52
57
 
@@ -57,7 +62,8 @@ class Conjur::Command::Users < Conjur::Command
57
62
  attributes = {
58
63
  "Login" => login,
59
64
  "Owner" => groupid,
60
- "UID Number" => uidnumber
65
+ "UID Number" => uidnumber,
66
+ "CIDR" => cidr
61
67
  }
62
68
  attributes["Password"] = "********" unless password.blank?
63
69
  prompt_to_confirm :user, attributes
@@ -70,6 +76,7 @@ class Conjur::Command::Users < Conjur::Command
70
76
  user_options = { }
71
77
  user_options[:ownerid] = groupid if groupid
72
78
  user_options[:uidnumber] = uidnumber.to_i if uidnumber
79
+ user_options[:cidr] = cidr unless cidr.nil?
73
80
  user_options[:password] = password if password
74
81
  user = api.create_user(login, user_options)
75
82
 
@@ -129,24 +136,57 @@ class Conjur::Command::Users < Conjur::Command
129
136
  c.flag [:p,:password]
130
137
 
131
138
  c.action do |global_options,options,args|
132
- username, password = Conjur::Authn.read_credentials
139
+ username, password = Conjur::Authn.get_credentials
133
140
  new_password = options[:password] || prompt_for_password
134
141
 
135
142
  Conjur::API.update_password username, password, new_password
136
143
  end
137
144
  end
138
145
 
139
- user.desc "Update user's attributes (only uidnumber supported now)"
140
- user.arg_name "USER"
146
+ user.desc "Rotate a user's API key"
147
+ user.command :rotate_api_key do |c|
148
+ c.desc "Login of user whose API key we want to rotate (default: logged-in user)"
149
+ c.flag [:user, :u]
150
+ c.action do |_global, options, _args|
151
+ if options.include?(:user)
152
+ # Make sure we're not trying to rotate our own key with the user flag.
153
+ if api.username == options[:user]
154
+ exit_now! 'To rotate your own API key, use this command without the --user flag'
155
+ end
156
+ puts api.user(options[:user]).rotate_api_key
157
+ else
158
+ username, password = Conjur::Authn.read_credentials
159
+ new_api_key = Conjur::API.rotate_api_key username, password
160
+ # Show the new one before saving credentials so we don't lose it on failure.
161
+ puts new_api_key
162
+ Conjur::Authn.save_credentials username: username, password: new_api_key
163
+ end
164
+ end
165
+ end
166
+
167
+ user.desc "Update a user's attributes"
168
+ user.arg_name "USER"
141
169
  user.command :update do |c|
142
- c.desc "UID number to be associated with user"
170
+ c.desc "UID number to be associated with user (optional)"
143
171
  c.flag [:uidnumber]
172
+
173
+ c.desc "A comma-delimited list of CIDR addresses to restrict user to (optional). Use 'all' to reset"
174
+ c.flag [:cidr]
175
+
144
176
  c.action do |global_options, options, args|
145
177
  login=require_arg(args,'USER')
146
- raise "Uidnumber should be integer" unless /\d+/ =~ options[:uidnumber]
147
- options[:uidnumber]=options[:uidnumber].to_i
148
- api.user(login).update(options)
149
- puts "UID set"
178
+
179
+ uidnumber = options[:uidnumber]
180
+ cidr = format_cidr(options[:cidr])
181
+
182
+ validate_uidnumber(uidnumber)
183
+
184
+ user_options = { }
185
+ user_options[:uidnumber] = uidnumber.to_i if uidnumber
186
+ user_options[:cidr] = cidr unless cidr.nil?
187
+
188
+ api.user(login).update(user_options)
189
+ puts "User updated"
150
190
  end
151
191
  end
152
192
 
@@ -165,4 +205,19 @@ class Conjur::Command::Users < Conjur::Command
165
205
  def self.prompt_for_uidnumber
166
206
  prompt_for_idnumber "uid number"
167
207
  end
208
+
209
+ def self.format_cidr(cidr)
210
+ case cidr
211
+ when 'all'
212
+ []
213
+ when nil
214
+ nil
215
+ else
216
+ cidr.split(',').each {|x| x.strip!}
217
+ end
218
+ end
219
+
220
+ def self.validate_uidnumber(uidnumber)
221
+ exit_now! 'uidnumber should be integer' unless uidnumber.blank? || /\d+/ =~ uidnumber
222
+ end
168
223
  end
@@ -153,24 +153,111 @@ class Conjur::Command::Variables < Conjur::Command
153
153
  $stdout.write api.variable(id).value(options[:version])
154
154
  end
155
155
  end
156
- end
157
-
158
- def self.prompt_for_kind
159
- highline.ask('Enter the kind: ') {|q| q.default = @default_kind }
160
- end
161
156
 
162
- def self.prompt_for_mime_type
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: ')
157
+ var.desc 'Set the expiration for a variable'
158
+ var.command :expire do |c|
159
+ c.arg_name "NOW"
160
+ c.desc 'Set variable to expire immediately'
161
+ min_version c, '4.6.0'
162
+ c.switch [:n, :'now'], :negatable => false
163
+
164
+ c.arg_name "DAYS"
165
+ c.desc 'Set variable to expire after the given number of days'
166
+ c.flag [:d, :'days']
167
+
168
+ c.arg_name "MONTHS"
169
+ c.desc 'Set variable to expire after the given number of months'
170
+ c.flag [:m, :'months']
171
+
172
+ c.arg_name "DURATION"
173
+ c.desc 'Set variable to expire after the given ISO8601 duration'
174
+ c.flag [:i, :'in']
175
+
176
+ c.action do |global_options, options, args|
177
+ id = require_arg(args, 'VARIABLE')
178
+
179
+ exit_now! 'Specify only one duration' if durations(options) > 1
180
+ exit_now! 'Specify at least one duration' if durations(options) == 0
181
+
182
+ now = options[:n]
183
+ days = options[:d]
184
+ months = options[:m]
185
+
186
+ case
187
+ when now.present?
188
+ duration = 'P0Y'
189
+ when days.present?
190
+ duration = "P#{days.to_i}D"
191
+ when months.present?
192
+ duration = "P#{months.to_i}M"
193
+ else
194
+ duration = options[:i]
195
+ end
196
+
197
+ display api.variable(id).expires_in(duration)
198
+ end
199
+ end
200
+
201
+ var.desc 'Display expiring variables'
202
+ var.long_desc 'Only variables that expire within the given duration are displayed. If no duration is provided, show all visible variables that are set to expire.'
203
+ var.command :expirations do |c|
204
+ c.arg_name 'DAYS'
205
+ c.desc 'Display variables that expire within the given number of days'
206
+ min_version c, '4.6.0'
207
+ c.flag [:d, :'days']
208
+
209
+ c.arg_name 'MONTHS'
210
+ c.desc 'Display variables that expire within the given number of months'
211
+ c.flag [:m, :'months']
212
+
213
+ c.arg_name 'IN'
214
+ c.desc 'Display variables that expire within the given ISO8601 interval'
215
+ c.flag [:i, :'in']
216
+
217
+ c.action do | global_options, options, args|
218
+
219
+ days = options[:d]
220
+ months = options[:m]
221
+ duration = options[:i]
222
+
223
+ exit_now! 'Specify only one duration' if durations(options) > 1
224
+
225
+ case
226
+ when days.present?
227
+ duration = "P#{days.to_i}D"
228
+ when months.present?
229
+ duration = "P#{months.to_i}M"
230
+ end
231
+
232
+ display api.variable_expirations(duration)
169
233
  end
170
234
  end
235
+
171
236
  end
172
237
 
173
- def self.prompt_for_value
174
- read_till_eof('Enter the secret value (^D on its own line to finish):')
238
+ class << self
239
+ def prompt_for_kind
240
+ highline.ask('Enter the kind: ') {|q| q.default = @default_kind }
241
+ end
242
+
243
+ def prompt_for_mime_type
244
+ highline.choose do |menu|
245
+ menu.prompt = 'Enter the MIME type: '
246
+ menu.choice @default_mime_type
247
+ menu.choices *%w(application/json application/xml application/x-yaml application/x-pem-file)
248
+ menu.choice "other", nil do |c|
249
+ @highline.ask('Enter a custom mime type: ')
250
+ end
251
+ end
252
+ end
253
+
254
+ def prompt_for_value
255
+ read_till_eof('Enter the secret value (^D on its own line to finish):')
256
+ end
257
+
258
+ def durations(options)
259
+ [options[:n],options[:d],options[:m],options[:i]].count {|o| o.present?}
260
+ end
175
261
  end
262
+
176
263
  end