conjur-cli 5.6.6 → 6.0.0.rc1

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 (203) hide show
  1. checksums.yaml +5 -5
  2. data/.dockerignore +1 -1
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +1 -1
  5. data/APPLIANCE_VERSION +1 -1
  6. data/CHANGELOG.md +3 -42
  7. data/Gemfile +4 -7
  8. data/Humanfile.md +31 -0
  9. data/Jenkinsfile +34 -63
  10. data/README.md +41 -55
  11. data/Rakefile +5 -1
  12. data/bin/conjur +0 -2
  13. data/build-deb.sh +1 -3
  14. data/ci/cli-test.sh +6 -0
  15. data/ci/package.sh +3 -1
  16. data/ci/publish.sh +2 -2
  17. data/ci/secrets/publish.yml +2 -2
  18. data/ci/wait_for_server.sh +10 -0
  19. data/conjur-cli.gemspec +7 -7
  20. data/dev/docker-compose.yml +24 -0
  21. data/dev/start.sh +15 -0
  22. data/dev/stop.sh +5 -0
  23. data/docker-compose.yml +30 -0
  24. data/features/authentication/authenticate.feature +34 -0
  25. data/features/authentication/login.feature +13 -0
  26. data/features/authentication/logout.feature +15 -0
  27. data/{acceptance-features → features}/authentication/whoami.feature +0 -0
  28. data/features/authorization/resource/annotate.feature +22 -0
  29. data/features/authorization/resource/check.feature +47 -0
  30. data/{acceptance-features → features}/authorization/resource/exists.feature +18 -6
  31. data/features/authorization/resource/permitted_roles.feature +35 -0
  32. data/features/authorization/resource/show.feature +34 -0
  33. data/features/authorization/role/exists.feature +28 -0
  34. data/features/authorization/role/members.feature +45 -0
  35. data/features/authorization/role/memberships.feature +43 -0
  36. data/features/conjurenv/check.feature +34 -0
  37. data/features/conjurenv/run.feature +15 -0
  38. data/{acceptance-features → features}/conjurenv/template.feature +8 -3
  39. data/{acceptance-features → features}/directory/user/update_password.feature +8 -2
  40. data/{acceptance-features → features}/directory/variable/value.feature +9 -5
  41. data/{acceptance-features → features}/directory/variable/values-add.feature +8 -3
  42. data/features/hostfactory/tokens.feature +22 -0
  43. data/features/pubkeys/show.feature +18 -0
  44. data/features/step_definitions/authn_steps.rb +22 -0
  45. data/features/step_definitions/cli_steps.rb +28 -0
  46. data/features/step_definitions/file_steps.rb +12 -0
  47. data/features/step_definitions/flow_control_steps.rb +7 -0
  48. data/features/step_definitions/graph_steps.rb +4 -3
  49. data/{acceptance-features → features}/step_definitions/http_steps.rb +0 -0
  50. data/features/step_definitions/overrides.rb +9 -0
  51. data/features/step_definitions/policy_steps.rb +11 -0
  52. data/{acceptance-features → features}/step_definitions/trusted_proxy_steps.rb +0 -0
  53. data/features/support/blank.yml +1 -0
  54. data/features/support/env.rb +21 -7
  55. data/features/support/hooks.rb +31 -116
  56. data/features/support/world.rb +16 -76
  57. data/jenkins.sh +33 -0
  58. data/lib/conjur/authenticator.rb +83 -0
  59. data/lib/conjur/authn.rb +5 -20
  60. data/lib/conjur/cli.rb +13 -6
  61. data/lib/conjur/command.rb +30 -350
  62. data/lib/conjur/command/authn.rb +23 -15
  63. data/lib/conjur/command/host_factories.rb +2 -74
  64. data/lib/conjur/command/hosts.rb +6 -113
  65. data/lib/conjur/command/init.rb +20 -35
  66. data/lib/conjur/command/{secrets.rb → policies.rb} +33 -22
  67. data/lib/conjur/command/pubkeys.rb +3 -63
  68. data/lib/conjur/command/resources.rb +45 -162
  69. data/lib/conjur/command/roles.rb +11 -181
  70. data/lib/conjur/command/rspec/helpers.rb +0 -1
  71. data/lib/conjur/command/rspec/mock_services.rb +4 -4
  72. data/lib/conjur/command/users.rb +2 -159
  73. data/lib/conjur/command/variables.rb +5 -218
  74. data/lib/conjur/complete.rb +2 -2
  75. data/lib/conjur/config.rb +1 -11
  76. data/lib/conjur/conjurenv.rb +12 -9
  77. data/lib/conjur/identifier_manipulation.rb +3 -5
  78. data/lib/conjur/version.rb +2 -2
  79. data/{publish-rubygem.sh → publish.sh} +0 -4
  80. data/spec/authn_spec.rb +4 -0
  81. data/spec/command/hosts_spec.rb +2 -69
  82. data/spec/command/init_spec.rb +16 -11
  83. data/spec/command/pubkeys_spec.rb +1 -46
  84. data/spec/command/resources_spec.rb +21 -170
  85. data/spec/command/roles_spec.rb +5 -181
  86. data/spec/command/users_spec.rb +3 -79
  87. data/spec/command_spec.rb +1 -20
  88. data/spec/complete_spec.rb +1 -23
  89. data/spec/config_spec.rb +1 -1
  90. data/spec/spec_helper.rb +4 -5
  91. data/test.sh +29 -25
  92. metadata +92 -212
  93. data/.githooks/pre_commit/run_specs.rb +0 -23
  94. data/Dockerfile +0 -15
  95. data/Dockerfile.fpm +0 -18
  96. data/Dockerfile.publish +0 -12
  97. data/Dockerfile.standalone +0 -33
  98. data/Dockerfile.validate-packaging +0 -9
  99. data/VERSION +0 -1
  100. data/acceptance-features/audit/audit_event_send.feature +0 -107
  101. data/acceptance-features/audit/fetch.feature +0 -16
  102. data/acceptance-features/audit/send.feature +0 -51
  103. data/acceptance-features/authentication/authenticate.feature +0 -10
  104. data/acceptance-features/authentication/login.feature +0 -12
  105. data/acceptance-features/authentication/logout.feature +0 -13
  106. data/acceptance-features/authorization/resource/annotate.feature +0 -35
  107. data/acceptance-features/authorization/resource/check.feature +0 -24
  108. data/acceptance-features/authorization/resource/create.feature +0 -21
  109. data/acceptance-features/authorization/resource/deny.feature +0 -12
  110. data/acceptance-features/authorization/resource/give.feature +0 -24
  111. data/acceptance-features/authorization/resource/permit.feature +0 -20
  112. data/acceptance-features/authorization/resource/permitted_roles.feature +0 -16
  113. data/acceptance-features/authorization/resource/show.feature +0 -28
  114. data/acceptance-features/authorization/role/create.feature +0 -13
  115. data/acceptance-features/authorization/role/exists.feature +0 -19
  116. data/acceptance-features/authorization/role/grant_to.feature +0 -21
  117. data/acceptance-features/authorization/role/graph.feature +0 -57
  118. data/acceptance-features/authorization/role/members.feature +0 -23
  119. data/acceptance-features/authorization/role/memberships.feature +0 -27
  120. data/acceptance-features/bootstrap.feature +0 -13
  121. data/acceptance-features/conjurenv/check.feature +0 -21
  122. data/acceptance-features/conjurenv/run.feature +0 -10
  123. data/acceptance-features/directory/group/create.feature +0 -20
  124. data/acceptance-features/directory/group/retire.feature +0 -54
  125. data/acceptance-features/directory/host/create.feature +0 -23
  126. data/acceptance-features/directory/host/retire.feature +0 -6
  127. data/acceptance-features/directory/hostfactory/create.feature +0 -28
  128. data/acceptance-features/directory/hostfactory/tokens.feature +0 -16
  129. data/acceptance-features/directory/layer/create.feature +0 -10
  130. data/acceptance-features/directory/layer/hosts-add.feature +0 -9
  131. data/acceptance-features/directory/layer/hosts-remove.feature +0 -10
  132. data/acceptance-features/directory/layer/retire.feature +0 -43
  133. data/acceptance-features/directory/user/create.feature +0 -23
  134. data/acceptance-features/directory/user/retire.feature +0 -6
  135. data/acceptance-features/directory/variable/create.feature +0 -14
  136. data/acceptance-features/directory/variable/retire.feature +0 -17
  137. data/acceptance-features/dsl/policy_owner.feature +0 -45
  138. data/acceptance-features/dsl/resource_owner.feature +0 -17
  139. data/acceptance-features/dsl/retire.feature +0 -15
  140. data/acceptance-features/global-privilege/elevate.feature +0 -20
  141. data/acceptance-features/global-privilege/reveal.privilege +0 -20
  142. data/acceptance-features/pubkeys/add.feature +0 -22
  143. data/acceptance-features/pubkeys/delete.feature +0 -9
  144. data/acceptance-features/pubkeys/names.feature +0 -26
  145. data/acceptance-features/pubkeys/show.feature +0 -27
  146. data/acceptance-features/step_definitions/cli_steps.rb +0 -57
  147. data/acceptance-features/step_definitions/graph_steps.rb +0 -22
  148. data/acceptance-features/step_definitions/user_steps.rb +0 -51
  149. data/acceptance-features/support/env.rb +0 -23
  150. data/acceptance-features/support/hooks.rb +0 -178
  151. data/acceptance-features/support/world.rb +0 -176
  152. data/acceptance-features/trusted_proxies.feature +0 -82
  153. data/bin/conjurize +0 -26
  154. data/bin/jsonfield +0 -70
  155. data/build-standalone +0 -6
  156. data/deprecations.sh +0 -38
  157. data/features/conjurize.feature +0 -134
  158. data/features/dsl_context.feature +0 -36
  159. data/features/dsl_host_create.feature +0 -11
  160. data/features/dsl_ownership.feature +0 -30
  161. data/features/dsl_permission.feature +0 -45
  162. data/features/dsl_resource_create.feature +0 -23
  163. data/features/dsl_role_create.feature +0 -11
  164. data/features/dsl_user_create.feature +0 -23
  165. data/features/jsonfield.feature +0 -49
  166. data/features/role_graph.feature +0 -58
  167. data/features/step_definitions/conjurize_steps.rb +0 -5
  168. data/features/step_definitions/dsl_steps.rb +0 -52
  169. data/features/support/conjur.conf +0 -6
  170. data/lib/conjur/command/assets.rb +0 -121
  171. data/lib/conjur/command/audit.rb +0 -155
  172. data/lib/conjur/command/bootstrap.rb +0 -129
  173. data/lib/conjur/command/dsl_command.rb +0 -75
  174. data/lib/conjur/command/elevate.rb +0 -76
  175. data/lib/conjur/command/field.rb +0 -45
  176. data/lib/conjur/command/groups.rb +0 -208
  177. data/lib/conjur/command/ids.rb +0 -34
  178. data/lib/conjur/command/layers.rb +0 -211
  179. data/lib/conjur/command/ldapsync.rb +0 -118
  180. data/lib/conjur/command/rspec/audit_helpers.rb +0 -68
  181. data/lib/conjur/command/rubydsl.rb +0 -93
  182. data/lib/conjur/command/script.rb +0 -48
  183. data/lib/conjur/command/server.rb +0 -67
  184. data/lib/conjur/conjurize.rb +0 -71
  185. data/lib/conjur/conjurize/script.rb +0 -150
  186. data/lib/conjur/dsl/runner.rb +0 -273
  187. data/publish-deb.sh +0 -6
  188. data/push-image +0 -29
  189. data/spec/command/assets_spec.rb +0 -115
  190. data/spec/command/audit_spec.rb +0 -376
  191. data/spec/command/elevate_spec.rb +0 -28
  192. data/spec/command/env_spec.rb +0 -168
  193. data/spec/command/groups_spec.rb +0 -77
  194. data/spec/command/host_factories_spec.rb +0 -38
  195. data/spec/command/layers_spec.rb +0 -35
  196. data/spec/command/ldapsync_spec.rb +0 -28
  197. data/spec/command/rubydsl_spec.rb +0 -63
  198. data/spec/command/variable_expiration_spec.rb +0 -164
  199. data/spec/command/variables_spec.rb +0 -192
  200. data/spec/conjurize/script_spec.rb +0 -62
  201. data/spec/conjurize_spec.rb +0 -70
  202. data/spec/dsl/runner_spec.rb +0 -93
  203. data/spec/env_spec.rb +0 -214
@@ -1,88 +1,28 @@
1
- require 'conjur/api'
2
- module ConjurWorld
3
- def last_json
4
- last_stdout
5
- end
6
-
7
- def last_stdout
8
- raise "No commands have been run" unless last_cmd
9
- stdout_from last_cmd
10
- end
11
-
12
- attr_accessor :last_cmd
13
-
14
- def account
15
- Conjur::Core::API.conjur_account
16
- end
17
-
18
- def role_kind
19
- @role_kind ||= "cli-cukes"
20
- end
21
-
22
- def role_id_map
23
- @role_id_map ||= {}
1
+ module CLIWorld
2
+ def tempfiles
3
+ @tempfiles ||= []
24
4
  end
25
5
 
26
- def extract_filtered_graph json
27
- graph = JSON.parse(json) if json.kind_of?(String)
28
- case graph
29
- when Hash then filter_hash_graph(graph)
30
- when Array then filter_array_graph(graph)
31
- else raise "WTF: graph was #{graph.class}?"
32
- end
33
- end
34
-
35
- def filter_hash_graph graph
36
- allowed = role_id_map.values
37
- edges = graph['graph']
38
- filtered = edges.select do |edge|
39
- allowed.member?(edge['parent']) and allowed.member?(edge['child'])
6
+ def api_key_of username
7
+ role = @policy_response.created_roles["cucumber:user:#{username}"]
8
+ if role
9
+ role['api_key']
10
+ else
11
+ $conjur.resource("cucumber:user:#{username}").rotate_api_key
40
12
  end
41
- {'graph' => filtered}
42
13
  end
43
-
44
- def filter_array_graph graph
45
- allowed = role_id_map.values
46
- graph.select do |edge|
47
- edge.all?{|v| allowed.member?(v)}
48
- end
49
- end
50
-
51
- def graph edges
52
- # generate roles
53
- edges.flatten.uniq.each do |role_id|
54
- role_id_map[role_id] = expanded = expand_role_id(role_id)
55
- run_command "conjur role create '#{expanded}'"
56
- end
57
14
 
58
- # generate memberships
59
- edges.each do |parent, child|
60
- run_command "conjur role grant_to #{expand_role_id(parent)} #{expand_role_id(child)}"
61
- end
15
+ def clear_last_json
16
+ @last_json = nil
62
17
  end
63
18
 
64
- def run_command cmd
65
- step "I successfully run " + '`' + cmd + '`'
19
+ def last_json
20
+ @last_json || last_command_started.stdout
66
21
  end
67
22
 
68
- def expand_role_id role_id
69
- "#{account}:#{role_kind}:#{prepend_namespace role_id}"
70
- end
71
-
72
- def prepend_namespace id
73
- "#{namespace}-#{id}"
74
- end
75
-
76
- def namespace
77
- @namespace ||= "ns-#{Time.now.to_i}-#{rand(1 << 32)}"
78
- end
79
-
80
- def expand_roles string
81
- role_id_map.each do |role, expanded|
82
- string.gsub! role, expanded
83
- end
84
- string
23
+ def load_policy id, policy, method
24
+ @policy_response = $conjur.load_policy id, policy, method: method
85
25
  end
86
26
  end
87
27
 
88
- World(ConjurWorld)
28
+ World(CLIWorld)
data/jenkins.sh ADDED
@@ -0,0 +1,33 @@
1
+ #!/bin/bash -ex
2
+
3
+ # Constants
4
+ RUBY_VERSION_DEFAULT="2.2.4"
5
+
6
+ # Arguments
7
+ RUBY_VERSION=${1-${RUBY_VERSION_DEFAULT}}
8
+
9
+ # Script
10
+
11
+ # Clones 'Dockerfile' and updates the Ruby version in FROM, returning the cloned file's path
12
+ function dockerfile_path {
13
+ echo "Setting Ruby version as ${RUBY_VERSION}" >&2
14
+ cp "Dockerfile" "Dockerfile.${RUBY_VERSION}"
15
+ sed -i -e "s/${RUBY_VERSION_DEFAULT}/${RUBY_VERSION}/g" Dockerfile.${RUBY_VERSION}
16
+
17
+ echo "Dockerfile.${RUBY_VERSION}"
18
+ }
19
+
20
+ rm -f Gemfile.lock # Needed for bundle to work right
21
+
22
+ IMAGE_NAME="cli-ruby:${RUBY_VERSION}" # The tag is the version of Ruby tested against
23
+
24
+ docker build -t ${IMAGE_NAME} -f $(dockerfile_path) .
25
+
26
+ docker run --rm \
27
+ -v $PWD:/src \
28
+ ${IMAGE_NAME} \
29
+ bash -c '''
30
+ bundle update
31
+ bundle exec rake jenkins
32
+ bundle exec rake build
33
+ '''
@@ -0,0 +1,83 @@
1
+ module Conjur
2
+ # Keeps a fresh Conjur access token in a named file by re-authenticating as needed.
3
+ class Authenticator
4
+ require 'tempfile'
5
+ require 'fileutils'
6
+
7
+ TOKEN_LIFESPAN = ( ENV['CONJUR_TOKEN_LIFESPAN'] || 5 * 60 ).to_i.seconds
8
+ DELAY = ( ENV['CONJUR_TOKEN_REFRESH_DELAY'] || 10 ).to_i.seconds
9
+
10
+ attr_reader :authenticate, :filename
11
+
12
+ # +authenticate+ should be a proc that authenticates with Conjur and returns an
13
+ # access token as a Hash.
14
+ def initialize authenticate, filename
15
+ @authenticate = authenticate
16
+ @filename = filename
17
+ end
18
+
19
+ class << self
20
+ def default_filename
21
+ "/run/conjur-access-token"
22
+ end
23
+
24
+ # Check the token every +DELAY+ seconds and refresh it if it's out of date.
25
+ def run authenticate:, filename: default_filename
26
+ while true
27
+ authenticator = Authenticator.new(authenticate, filename)
28
+ authenticator.refresh unless authenticator.fresh?
29
+ sleep DELAY
30
+ end
31
+ end
32
+ end
33
+
34
+ def fresh?
35
+ token && (token_age <= TOKEN_LIFESPAN)
36
+ end
37
+
38
+ # Perform atomic replacement of the token
39
+ def refresh
40
+ token = authenticate.call
41
+ file = Tempfile.new('conjur-access-token.')
42
+ begin
43
+ file.write JSON.pretty_generate(token)
44
+ file.close
45
+ FileUtils.mv file.path, filename
46
+ Conjur.log << "Refreshed Conjur auth token to #{filename.inspect}\n" if Conjur.log
47
+ ensure
48
+ file.unlink
49
+ end
50
+ rescue
51
+ $stderr.puts $!
52
+ end
53
+
54
+ def token
55
+ return false if @token == false
56
+ @token ||= load_token
57
+ end
58
+
59
+ protected
60
+
61
+ def random nbytes = 12
62
+ @random ||= Random.new
63
+ @random.bytes(nbytes).unpack('h*').first
64
+ end
65
+
66
+ def directory
67
+ File.dirname(filename)
68
+ end
69
+
70
+ def load_token
71
+ return false unless File.file?(filename)
72
+ JSON.parse(File.read(filename)) rescue false
73
+ end
74
+
75
+ def token_born
76
+ File.mtime(filename)
77
+ end
78
+
79
+ def token_age
80
+ Time.now - token_born
81
+ end
82
+ end
83
+ end
data/lib/conjur/authn.rb CHANGED
@@ -34,7 +34,6 @@ module Conjur::Authn
34
34
  end
35
35
  end
36
36
 
37
- autoload :API, 'conjur/authn-api'
38
37
  class << self
39
38
  def login(options = {})
40
39
  delete_credentials
@@ -43,18 +42,14 @@ module Conjur::Authn
43
42
 
44
43
  def authenticate(options = {})
45
44
  require 'conjur/api'
46
- Conjur::API.authenticate(*get_credentials(options))
45
+ Conjur::API.authenticate *get_credentials(options)
47
46
  end
48
47
 
49
48
  def delete_credentials
50
- netrc.delete host
49
+ netrc.delete Conjur.configuration.authn_url
51
50
  netrc.save
52
51
  end
53
52
 
54
- def host
55
- Conjur::Authn::API.host
56
- end
57
-
58
53
  def netrc
59
54
  @netrc ||= read_netrc
60
55
  end
@@ -83,7 +78,7 @@ module Conjur::Authn
83
78
  end
84
79
 
85
80
  def read_credentials
86
- netrc[host]
81
+ netrc[Conjur.configuration.authn_url]
87
82
  end
88
83
 
89
84
  def fetch_credentials(options = {})
@@ -94,7 +89,7 @@ module Conjur::Authn
94
89
  alias save_credentials fetch_credentials
95
90
 
96
91
  def write_credentials
97
- netrc[host] = @credentials
92
+ netrc[Conjur.configuration.authn_url] = @credentials
98
93
  netrc.save
99
94
  @credentials
100
95
  end
@@ -128,10 +123,8 @@ module Conjur::Authn
128
123
  end
129
124
  if token = token_from_environment
130
125
  cls.new_from_token token
131
- elsif token_file = token_from_file
132
- cls.new_from_token_file token_file
133
126
  else
134
- cls.new_from_key(*get_credentials(options))
127
+ cls.new_from_key *get_credentials(options)
135
128
  end
136
129
  end
137
130
 
@@ -155,13 +148,5 @@ module Conjur::Authn
155
148
  require 'base64'
156
149
  JSON.parse(Base64.decode64(token))
157
150
  end
158
-
159
- def token_from_file
160
- token_file = ENV['CONJUR_AUTHN_TOKEN_FILE']
161
- if token_file && !File.exists?(token_file)
162
- $stderr.puts "Warning: CONJUR_AUTHN_TOKEN_FILE #{token_file.inspect} does not exist"
163
- end
164
- token_file
165
- end
166
151
  end
167
152
  end
data/lib/conjur/cli.rb CHANGED
@@ -37,6 +37,7 @@ module Conjur
37
37
  autoload :Log, 'conjur/log'
38
38
  autoload :IdentifierManipulation, 'conjur/identifier_manipulation'
39
39
  autoload :Authn, 'conjur/authn'
40
+ autoload :Authenticator, 'conjur/authenticator'
40
41
  autoload :Command, 'conjur/command'
41
42
  autoload :DSL, 'conjur/dsl/runner'
42
43
  autoload :DSLCommand, 'conjur/command/dsl_command'
@@ -117,7 +118,7 @@ module Conjur
117
118
  require 'conjur/api'
118
119
 
119
120
  if command.name_for_help.first == "init" and options.has_key?("account")
120
- ENV["CONJUR_ACCOUNT"]=options["account"]
121
+ ENV["CONJUR_ACCOUNT"] = options["account"]
121
122
  end
122
123
  apply_config
123
124
  require 'active_support/core_ext'
@@ -154,12 +155,18 @@ module Conjur
154
155
  $stderr.puts "error: this command is not supported by the current Conjur server version"
155
156
  run_default_handler = false
156
157
  elsif exception.is_a?(RestClient::Exception) && exception.response
157
- err = Conjur::Error.create exception.response.body
158
- if err
159
- $stderr.puts "error: " + err.message
160
- run_default_handler = false # suppress default error message
158
+ if exception.http_code == 401
159
+ $stderr.puts "Unable to authenticate with Conjur. Please check your credentials."
160
+ run_default_handler = false
161
161
  else
162
- $stderr.puts exception.response.body
162
+ err = Conjur::Error.create exception.response.body
163
+
164
+ if err
165
+ $stderr.puts "error: " + err.message
166
+ run_default_handler = false # suppress default error message
167
+ else
168
+ $stderr.puts exception.response.body
169
+ end
163
170
  end
164
171
  end
165
172
 
@@ -19,7 +19,6 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  require 'base64'
22
- require 'semantic'
23
22
 
24
23
  module Conjur
25
24
  class Command
@@ -29,7 +28,7 @@ module Conjur
29
28
 
30
29
  class << self
31
30
  attr_accessor :prefix
32
-
31
+
33
32
  def method_missing *a, &b
34
33
  Conjur::CLI.send *a, &b
35
34
  end
@@ -70,61 +69,12 @@ module Conjur
70
69
  def command.nodoc; true end
71
70
  end
72
71
 
73
- def acting_as_option command
74
- return if command.flags.member?(:"as-group") # avoid duplicate flags
75
- command.desc 'Perform all actions as the specified Group'
76
- command.arg_name 'GROUP'
77
- command.flag [:'as-group']
78
-
79
- command.desc 'Perform all actions as the specified Role'
80
- command.arg_name 'ROLE'
81
- command.flag [:'as-role']
82
- end
83
-
84
- def collection_option command
85
- command.desc 'An optional prefix for created roles and resources'
86
- command.arg_name 'collection'
87
- command.flag [:collection]
88
- end
89
-
90
72
  def context_option command
91
73
  command.desc "Load context from this config file, and save it when finished. The file permissions will be 0600 by default."
92
74
  command.arg_name "FILE"
93
75
  command.flag [:c, :context]
94
76
  end
95
77
 
96
- def interactive_option command
97
- command.arg_name 'interactive'
98
- command.desc 'Create variable interactively'
99
- command.switch [:i, :'interactive']
100
- end
101
-
102
- def annotate_option command
103
- command.arg_name 'annotate'
104
- command.desc 'Add variable annotations interactively'
105
- command.switch [:a, :annotate]
106
- end
107
-
108
- def min_version command, version
109
- version = Semantic::Version.new version
110
- if version.pre == nil
111
- # Version check doesn't work correctly if one version has
112
- # the "-###" suffix and the other does not. Versions
113
- # returned by the server have the suffix.
114
- version.pre = "0"
115
- end
116
- command.instance_variable_set(:@conjur_min_version, version)
117
- end
118
-
119
- def prompt_for_annotations
120
- highline.say('Add annotations (a name and value for each one):')
121
- {}.tap do |annotations|
122
- until (name = highline.ask(' annotation name (press enter to quit annotations): ')).empty?
123
- annotations[name] = read_till_eof(' annotation value (^D on its own line to finish):')
124
- end
125
- end
126
- end
127
-
128
78
  def highline
129
79
  require 'highline'
130
80
  @highline ||= HighLine.new($stdin,$stderr)
@@ -142,62 +92,35 @@ module Conjur
142
92
  end
143
93
  end.join("\n")
144
94
  end
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
95
+
96
+ def command_options_for_list(c)
152
97
  return if c.flags.member?(:role) # avoid duplicate flags
153
98
  c.desc "Role to act as. By default, the current logged-in role is used."
154
99
  c.arg_name 'ROLE'
155
100
  c.flag [:role]
156
- end
157
-
158
- def command_options_for_search c
101
+
159
102
  c.desc "Full-text search on resource id and annotation values"
160
103
  c.flag [:s, :search]
161
104
 
105
+ c.desc "Maximum number of records to return"
106
+ c.flag [:l, :limit]
107
+
162
108
  c.desc "Offset to start from"
163
109
  c.flag [:o, :offset]
164
110
 
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
-
176
- c.desc "Show only ids"
177
- c.switch [:i, :ids]
178
-
111
+ c.desc "Show full object information"
112
+ c.switch [:inspect]
113
+
179
114
  c.desc "Show annotations in 'raw' format"
180
115
  c.switch [:r, :"raw-annotations"]
181
116
  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
192
117
 
193
118
  def command_impl_for_list(global_options, options, args)
194
- opts = process_command_options_for_search(options)
119
+ opts = options.slice(:search, :limit, :options, :kind)
120
+ opts[:acting_as] = options[:role] if options[:role]
121
+ opts[:search]=opts[:search].gsub('-',' ') if opts[:search]
195
122
  resources = api.resources(opts)
196
- if resources.is_a?(Numeric)
197
- puts resources
198
- elsif options[:ids]
199
- puts JSON.pretty_generate(resources.map(&:resourceid))
200
- else
123
+ if options[:inspect]
201
124
  resources = resources.map &:attributes
202
125
  unless options[:'raw-annotations']
203
126
  resources = resources.map do |r|
@@ -209,6 +132,8 @@ module Conjur
209
132
  end
210
133
  end
211
134
  puts JSON.pretty_generate resources
135
+ else
136
+ puts JSON.pretty_generate(resources.map(&:id))
212
137
  end
213
138
  end
214
139
 
@@ -220,160 +145,24 @@ module Conjur
220
145
  end
221
146
  exit_now! message unless valid
222
147
  end
223
-
224
- def retire_options command
225
- command.arg_name 'role'
226
- command.desc "Specify a role to give the retired record to (default: the 'attic' user)"
227
- command.long_desc %Q(When retired, all a record's roles and permissions are revoked.
228
-
229
- As a final step, the record is 'given' (e.g. 'conjur resource give') to a destination role.
230
- The default role to receive the record is the user 'attic'. This option can be used to specify
231
- an alternative destination role.)
232
- command.flag [:d, :"destination-role"]
233
- end
234
-
235
- def destination_role options
236
- destination = options[:"destination-role"]
237
- if destination
238
- api.role(destination)
239
- else
240
- api.user('attic')
241
- end
242
- end
243
-
244
- def elevated?
245
- api.privilege == 'elevate' && api.global_privilege_permitted?('elevate')
246
- end
247
-
248
- def validate_retire_privileges record, options
249
- return true if elevated?
250
-
251
- if record.respond_to?(:role)
252
- memberships = current_user.role.memberships.map(&:roleid)
253
- validate_privileges "You can't administer this record" do
254
- # The current user has a role which is admin of the record's role
255
- record.role.members.find{|m| memberships.member?(m.member.roleid) && m.admin_option}
256
- end
257
- end
258
-
259
- validate_privileges "You don't own the record" do
260
- # The current user has the role which owns the record's resource
261
- current_user.role.member_of?(record.resource.ownerid)
262
- end
263
-
264
- role = destination_role(options)
265
- exit_now! "Destination role '#{role.roleid}' doesn't exist" unless role.exists?
266
- end
267
-
268
- def retire_resource obj
269
- obj.resource.attributes['permissions'].each do |p|
270
- role = api.role(p['role'])
271
- privilege = p['privilege']
272
- next if obj.respond_to?(:roleid) && role.roleid == obj.roleid && privilege == 'read'
273
- puts "Denying #{privilege} privilege to #{role.roleid}"
274
- obj.resource.deny(privilege, role)
275
- end
276
- end
277
-
278
- def retire_role obj
279
- members = obj.role.members
280
- # Move the invoking role to the end of the roles list, so that it doesn't
281
- # lose its permissions in the middle of this operation.
282
- # I'm sure there's a cleaner way to do this.
283
- self_member = members.select{|m| m.member.roleid == current_user.role.roleid}
284
- self_member.each do |m|
285
- members.delete m
286
- end
287
- members.concat self_member if self_member
288
- members.each do |r|
289
- member = api.role(r.member)
290
- puts "Revoking from role #{member.roleid}"
291
- obj.role.revoke_from member
292
- end
293
- end
294
-
295
- def retire_internal_role roleObj
296
- members = roleObj.members
297
- # Move the invoking role to the end of the roles list, so that it doesn't
298
- # lose its permissions in the middle of this operation.
299
- self_member = members.select{|m| m.member.roleid == current_user.role.roleid}
300
- self_member.each do |m|
301
- members.delete m
302
- end
303
- members.concat self_member if self_member
304
- members.each do |r|
305
- member = api.role(r.member)
306
- puts "Revoking from role #{member.roleid}"
307
- roleObj.revoke_from member
308
- end
309
- end
310
-
311
- def give_away_resource obj, options
312
- destination = options[:"destination-role"]
313
- destination_role = if destination
314
- api.role(destination)
315
- else
316
- api.user('attic')
317
- end
318
148
 
319
- exit_now! "Role #{destination_role.roleid} doesn't exist" unless destination_role.exists?
320
-
321
- puts "Giving ownership to '#{destination_role.roleid}'"
322
- obj.resource.give_to destination_role
323
- end
324
-
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
149
+ def display_members(members, options)
150
+ result = if options[:V]
151
+ members.collect {|member|
152
+ {
153
+ role: member.role.id,
154
+ member: member.member.id,
155
+ admin_option: member.admin_option
156
+ }
157
+ }
360
158
  else
361
- [ members.map(&:roleid), lambda{|r| r} ]
159
+ members.map(&:member).map(&:id)
362
160
  end
363
-
364
- unless options[:system]
365
- result.reject! do |member|
366
- roleid_function.call(member) =~ /^.+?:@/
367
- end
368
- end
369
-
370
161
  display result
371
162
  end
372
163
 
373
164
  def display(obj, options = {})
374
- str = if obj.is_a?(Numeric)
375
- obj.to_s
376
- elsif obj.respond_to?(:attributes)
165
+ str = if obj.respond_to?(:attributes)
377
166
  JSON.pretty_generate obj.attributes
378
167
  elsif obj.respond_to?(:id)
379
168
  obj.id
@@ -387,105 +176,9 @@ an alternative destination role.)
387
176
  puts str
388
177
  end
389
178
 
390
- def prompt_to_confirm kind, properties
391
- puts
392
- puts "A new #{kind} will be created with the following properties:"
393
- puts
394
- properties.select{|k,v| !v.blank?}.each do |k,v|
395
- printf "%-10s: %s\n", k, v
396
- end
397
- puts
398
-
399
- exit(0) unless %w(yes y).member?(highline.ask("Proceed? (yes/no): ").strip.downcase)
400
- end
401
-
402
179
  def integer? v
403
180
  Integer(v, 10) rescue false
404
181
  end
405
-
406
- def prompt_for_id kind, label = 'id'
407
- highline.ask("Enter the #{label}: ") do |q|
408
- q.readline = true
409
- q.validate = lambda{|id|
410
- !id.blank? && !api.send(kind, id).exists?
411
- }
412
- q.responses[:not_valid] = "<% if @answer.blank? %>"\
413
- "#{label} cannot be blank<% else %>"\
414
- "A #{kind} called '<%= @answer %>' already exists<% end %>"
415
- end
416
- end
417
-
418
- def prompt_for_public_key
419
- public_key = highline.ask("Enter the public key (press enter to skip): ") do |q|
420
- q.validate = lambda{|key|
421
- if key.blank?
422
- true
423
- else
424
- validate_public_key key
425
- end
426
- }
427
- q.responses[:not_valid] = "Public key format is invalid; please try again"
428
- end
429
- public_key.blank? ? nil : public_key.strip
430
- end
431
-
432
- # http://serverfault.com/questions/453296/how-do-i-validate-a-rsa-ssh-public-key-file-id-rsa-pub
433
- def validate_public_key key
434
- if system('which ssh-keygen 2>&1 > /dev/null')
435
- Conjur.log.debug "Using ssh-keygen to verify the public key\n" if Conjur.log
436
- require 'tempfile'
437
- tempfile = Tempfile.new 'public_key'
438
- tempfile.write(key)
439
- tempfile.close
440
- `ssh-keygen -l -f #{tempfile.path}`
441
- $? == 0
442
- else
443
- Conjur.log.debug "ssh-keygen is not available; falling back to simple string testing\n" if Conjur.log
444
- # Should be a line with at least 2 components,
445
- # first one being the algo id and second a base64 string.
446
- # In principle this means:
447
- # Base64.strict_decode64 key.strip[/\Assh-\w+ (\S+).*/, 1]
448
-
449
- # Since the pubkeys service is more strict: needs a name and
450
- # rejects ones with a space, instead reproduce its algorithm here.
451
- begin
452
- components = key.strip.split ' '
453
- Base64.strict_decode64 components[1]
454
- components.length == 3
455
- rescue NoMethodError, ArgumentError
456
- false
457
- end
458
- end
459
- end
460
-
461
- def prompt_for_group options = {}
462
- options[:hint] ||= "press enter to own the record yourself"
463
- group_ids = api.groups.map(&:id)
464
-
465
- highline.ask("Enter the group which will own the record (#{options[:hint]}): ", [ "" ] + group_ids) do |q|
466
- require 'readline'
467
- Readline.completion_append_character = ""
468
- Readline.completer_word_break_characters = ""
469
-
470
- q.readline = true
471
- q.validate = lambda{|id|
472
- @group = nil
473
- id.empty? || (@group = api.group(id)).exists?
474
- }
475
- q.responses[:not_valid] = "Group '<%= @answer %>' doesn't exist, or you don't have permission to use it"
476
- end
477
- @group ? @group.roleid : nil
478
- end
479
-
480
- def prompt_for_idnumber label
481
- result = highline.ask("Enter a #{label}: ") do |q|
482
- q.validate = lambda{|id|
483
- id.blank? || integer?(id)
484
- }
485
- q.responses[:not_valid] = "The #{label} must be an integer"
486
- end
487
- result.blank? ? nil : result.to_i
488
- end
489
182
 
490
183
  def prompt_for_password
491
184
  require 'highline'
@@ -509,27 +202,14 @@ an alternative destination role.)
509
202
  password
510
203
  end
511
204
 
512
- def current_role
513
- kind, id = api.username.split('/', 2)
514
- if id.nil?
515
- id = kind
516
- kind = 'user'
517
- end
518
- api.role([ kind, id ].join(":"))
519
- end
520
-
521
205
  def has_admin?(role, other_role)
522
- return true if role.roleid == other_role.roleid
523
- memberships = role.memberships.map(&:roleid)
524
- other_role.members.any? { |m| memberships.member?(m.member.roleid) && m.admin_option }
206
+ return true if role.id == other_role.id
207
+ memberships = role.memberships.map(&:id)
208
+ other_role.members.any? { |m| memberships.member?(m.member.id) && m.admin_option }
525
209
  rescue RestClient::Forbidden
526
210
  false
527
211
  end
528
212
 
529
- def notify_deprecated
530
- STDERR.puts 'WARNING! This command is deprecated and will be removed. Use policy instead.'
531
- end
532
-
533
213
  end
534
214
  end
535
215
  end