conjur-cli 5.6.6 → 6.0.0.rc1

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