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
data/build-deb.sh ADDED
@@ -0,0 +1,19 @@
1
+ #!/bin/bash -ex
2
+
3
+ export DEBUG=true
4
+ export GLI_DEBUG=true
5
+
6
+ # Make sure Gemfile.lock exists
7
+ gem install -N bundler
8
+ bundle
9
+
10
+ debify clean
11
+
12
+ debify package \
13
+ --dockerfile ci/Dockerfile.fpm \
14
+ cli \
15
+ -- \
16
+ --depends ruby2.0
17
+
18
+ debify test -t 4.6-stable cli ci/test.sh
19
+
data/ci/test.sh ADDED
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+
3
+ conjur_cid="$1"
4
+
5
+ cat << TEST | docker exec -i $conjur_cid bash
6
+ #!/bin/bash -ex
7
+
8
+ cd /src/cli
9
+
10
+ unset CONJUR_AUTHN_LOGIN
11
+
12
+ bundle exec rake jenkins || true
13
+
14
+ env CONJUR_AUTHN_LOGIN=admin CONJUR_AUTHN_API_KEY=secret bundle exec cucumber -r acceptance-features/support \
15
+ -r acceptance-features/step_definitions \
16
+ -f pretty \
17
+ -f junit --out acceptance-features/reports \
18
+ acceptance-features || true
19
+ TEST
data/conjur.gemspec CHANGED
@@ -16,26 +16,23 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Conjur::VERSION
17
17
 
18
18
 
19
- gem.add_dependency 'activesupport'
20
- gem.add_dependency 'conjur-api', '~> 4.19'
19
+ gem.add_dependency 'activesupport', '~> 4.2'
20
+ gem.add_dependency 'conjur-api', '~> 4.20'
21
21
  gem.add_dependency 'gli', '>=2.8.0'
22
- gem.add_dependency 'highline'
22
+ gem.add_dependency 'highline', '~> 1.7'
23
23
  gem.add_dependency 'netrc', '~> 0.10.2'
24
- gem.add_dependency 'methadone'
25
- gem.add_dependency 'deep_merge'
26
- gem.add_dependency 'xdg'
24
+ gem.add_dependency 'methadone', '~> 1.9'
25
+ gem.add_dependency 'deep_merge', '~> 1.0'
26
+ gem.add_dependency 'xdg', '~> 2.2'
27
27
 
28
- gem.add_runtime_dependency 'cas_rest_client'
28
+ gem.add_runtime_dependency 'cas_rest_client', '~> 1.3'
29
29
 
30
30
  gem.add_development_dependency 'rspec', '~> 3.0'
31
31
  gem.add_development_dependency 'simplecov'
32
- gem.add_development_dependency 'aruba', '~> 0.6.1'
32
+ gem.add_development_dependency 'aruba', '~> 0.12.0'
33
33
  gem.add_development_dependency 'ci_reporter_rspec', '~> 1.0'
34
- gem.add_development_dependency 'ci_reporter_cucumber'
34
+ gem.add_development_dependency 'ci_reporter_cucumber', '~> 1.0'
35
35
  gem.add_development_dependency 'rake', '~> 10.0'
36
36
  gem.add_development_dependency 'io-grab', '~> 0.0.1'
37
37
  gem.add_development_dependency 'json_spec'
38
- # For cukes
39
- gem.add_development_dependency 'conjur-asset-audit-send'
40
- gem.add_development_dependency 'conjur-asset-host-factory'
41
38
  end
data/debify.sh ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/bash -ex
2
+
3
+ mkdir -p usr/local/bin
4
+ cp -a opt/conjur/cli/distrib/bin/* usr/local/bin
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ conjur-plugin-service cli ./bin/_conjur "$@"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ conjur-plugin-service cli ./bin/conjur "$@"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ conjur-plugin-service cli ./bin/conjurize "$@"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ conjur-plugin-service cli ./bin/jsonfield "$@"
@@ -11,22 +11,22 @@ Feature: conjurize program generates install scripts
11
11
 
12
12
  Scenario: Minimal conjurize script
13
13
  When I conjurize ""
14
- Then the stdout should contain exactly:
14
+ Then the stdout should contain:
15
15
  """
16
16
  #!/bin/sh
17
17
  set -e
18
18
 
19
19
  # Implementation note: 'tee' is used as a sudo-friendly 'cat' to populate a file with the contents provided below.
20
20
 
21
- tee /etc/conjur.conf > /dev/null << CONJUR_CONF
21
+ tee /etc/conjur.conf > /dev/null << EOF
22
22
  account: test
23
23
  appliance_url: https://conjur/api
24
24
  cert_file: /etc/conjur-test.pem
25
25
  netrc_path: /etc/conjur.identity
26
26
  plugins: []
27
- CONJUR_CONF
27
+ EOF
28
28
 
29
- tee /etc/conjur-test.pem > /dev/null << CONJUR_CERT
29
+ tee /etc/conjur-test.pem > /dev/null << EOF
30
30
  -----BEGIN CERTIFICATE-----
31
31
  MIIDZTCCAk2gAwIBAgIJAMzfPBZBq82XMA0GCSqGSIb3DQEBBQUAMDMxMTAvBgNV
32
32
  BAMTKGVjMi01NC04My05OS0xMzUuY29tcHV0ZS0xLmFtYXpvbmF3cy5jb20wHhcN
@@ -48,15 +48,15 @@ yvml0YdVSiMdTdIk58qG84pkmteSX9VYE1IF7xfWb3ji8292fm5q6cgqFLNYx2MI
48
48
  MVs0y+HobWbOKKhyfxpMT59dJxGu21QPbWfQLkHCCOlo2P4z9oku23sbvQQ7CbvS
49
49
  VoykXurdaZo9
50
50
  -----END CERTIFICATE-----
51
- CONJUR_CERT
51
+ EOF
52
52
 
53
- tee /etc/conjur.identity > /dev/null << CONJUR_IDENTITY
53
+ touch /etc/conjur.identity
54
+ chmod 600 /etc/conjur.identity
55
+ tee /etc/conjur.identity > /dev/null << EOF
54
56
  machine https://conjur/api/authn
55
- login host/ec2/i-eaa5f700
56
- password 3a4rb19rpjejr89h6r29kd2fb3808cpy
57
- CONJUR_IDENTITY
58
- chmod 0600 /etc/conjur.identity
59
-
57
+ login host/ec2/i-eaa5f700
58
+ password 3a4rb19rpjejr89h6r29kd2fb3808cpy
59
+ EOF
60
60
  """
61
61
 
62
62
  Scenario: conjurize with SSH installation
@@ -68,15 +68,15 @@ set -e
68
68
 
69
69
  # Implementation note: 'tee' is used as a sudo-friendly 'cat' to populate a file with the contents provided below.
70
70
 
71
- tee /etc/conjur.conf > /dev/null << CONJUR_CONF
71
+ tee /etc/conjur.conf > /dev/null << EOF
72
72
  account: test
73
73
  appliance_url: https://conjur/api
74
74
  cert_file: /etc/conjur-test.pem
75
75
  netrc_path: /etc/conjur.identity
76
76
  plugins: []
77
- CONJUR_CONF
77
+ EOF
78
78
 
79
- tee /etc/conjur-test.pem > /dev/null << CONJUR_CERT
79
+ tee /etc/conjur-test.pem > /dev/null << EOF
80
80
  -----BEGIN CERTIFICATE-----
81
81
  MIIDZTCCAk2gAwIBAgIJAMzfPBZBq82XMA0GCSqGSIb3DQEBBQUAMDMxMTAvBgNV
82
82
  BAMTKGVjMi01NC04My05OS0xMzUuY29tcHV0ZS0xLmFtYXpvbmF3cy5jb20wHhcN
@@ -98,17 +98,17 @@ yvml0YdVSiMdTdIk58qG84pkmteSX9VYE1IF7xfWb3ji8292fm5q6cgqFLNYx2MI
98
98
  MVs0y+HobWbOKKhyfxpMT59dJxGu21QPbWfQLkHCCOlo2P4z9oku23sbvQQ7CbvS
99
99
  VoykXurdaZo9
100
100
  -----END CERTIFICATE-----
101
- CONJUR_CERT
101
+ EOF
102
102
 
103
- tee /etc/conjur.identity > /dev/null << CONJUR_IDENTITY
103
+ touch /etc/conjur.identity
104
+ chmod 600 /etc/conjur.identity
105
+ tee /etc/conjur.identity > /dev/null << EOF
104
106
  machine https://conjur/api/authn
105
- login host/ec2/i-eaa5f700
106
- password 3a4rb19rpjejr89h6r29kd2fb3808cpy
107
- CONJUR_IDENTITY
108
- chmod 0600 /etc/conjur.identity
107
+ login host/ec2/i-eaa5f700
108
+ password 3a4rb19rpjejr89h6r29kd2fb3808cpy
109
+ EOF
109
110
 
110
111
  curl -L https://www.opscode.com/chef/install.sh | bash
111
-
112
112
  """
113
113
  And the output should match:
114
114
  """
@@ -126,9 +126,9 @@ curl -L https://www.opscode.com/chef/install.sh | bash
126
126
 
127
127
  Scenario: conjurize with sudo-ized commands
128
128
  When I conjurize "--sudo --ssh"
129
- Then the stdout should contain "sudo -n tee /etc/conjur.conf > /dev/null << CONJUR_CONF"
130
- And the stdout should contain "sudo -n tee /etc/conjur-test.pem > /dev/null << CONJUR_CERT"
131
- And the stdout should contain "sudo -n tee /etc/conjur.identity > /dev/null << CONJUR_IDENTITY"
132
- And the stdout should contain "sudo -n chmod 0600 /etc/conjur.identity"
129
+ Then the stdout should contain "sudo -n tee /etc/conjur.conf > /dev/null << EOF"
130
+ And the stdout should contain "sudo -n tee /etc/conjur-test.pem > /dev/null << EOF"
131
+ And the stdout should contain "sudo -n tee /etc/conjur.identity > /dev/null << EOF"
132
+ And the stdout should contain "sudo -n chmod 600 /etc/conjur.identity"
133
133
  And the stdout should contain "curl -L https://www.opscode.com/chef/install.sh | sudo -n bash"
134
134
 
@@ -4,5 +4,9 @@ require 'methadone/cucumber'
4
4
  require 'cucumber/rspec/doubles'
5
5
  require "json_spec/cucumber"
6
6
 
7
-
8
7
  SimpleCov.start
8
+
9
+ Aruba.configure do |config|
10
+ config.exit_timeout = 15
11
+ config.io_wait_timeout = 2
12
+ end
@@ -124,5 +124,4 @@ end
124
124
  Before('@real-api') do
125
125
  Conjur::Config.load
126
126
  Conjur::Config.apply
127
- @aruba_timeout_seconds = 15
128
127
  end
data/jenkins.sh CHANGED
@@ -1,5 +1,33 @@
1
- #!/bin/bash -e
1
+ #!/bin/bash -ex
2
2
 
3
+ # Constants
4
+ RUBY_VERSION_DEFAULT="2.1.5"
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 '''
3
30
  bundle update
4
31
  bundle exec rake jenkins
5
32
  bundle exec rake build
33
+ '''
data/lib/conjur/cli.rb CHANGED
@@ -49,6 +49,8 @@ module Conjur
49
49
  class CLI
50
50
  extend GLI::App
51
51
 
52
+ @current_command = nil
53
+
52
54
  class << self
53
55
  def load_config
54
56
  Conjur::Config.load
@@ -69,8 +71,7 @@ module Conjur
69
71
 
70
72
  def load_plugins
71
73
  # These used to be plugins but now they are in the core CLI
72
- plugins = Conjur::Config.plugins - %w(layer pubkeys)
73
-
74
+ plugins = Conjur::Config.plugins - %w(audit-send host-factory layer pubkeys)
74
75
  plugins.each do |plugin|
75
76
  begin
76
77
  filename = "conjur-asset-#{plugin}"
@@ -92,6 +93,19 @@ module Conjur
92
93
  load_plugins
93
94
  commands_from 'conjur/command'
94
95
  end
96
+
97
+ def appliance_version
98
+ Conjur::API.service_version 'appliance'
99
+ rescue
100
+ nil
101
+ end
102
+
103
+ def command_version_compatible? command
104
+ !command.instance_variable_defined?(:@conjur_min_version) ||
105
+ (appliance_version &&
106
+ command.instance_variable_get(:@conjur_min_version) <= appliance_version
107
+ )
108
+ end
95
109
  end
96
110
 
97
111
  init!
@@ -123,13 +137,22 @@ module Conjur
123
137
 
124
138
  true
125
139
  end
140
+
141
+ around do |global,command,options,args,code|
142
+ @current_command = command
143
+ code.call
144
+ @current_command = nil
145
+ end
126
146
 
127
147
  on_error do |exception|
128
148
  require 'rest-client'
129
149
  require 'patches/conjur/error'
130
-
150
+
131
151
  run_default_handler = true
132
- if exception.is_a?(RestClient::Exception) && exception.response
152
+ if @current_command != nil && !command_version_compatible?(@current_command)
153
+ $stderr.puts "error: this command is not supported by the current Conjur server version"
154
+ run_default_handler = false
155
+ elsif exception.is_a?(RestClient::Exception) && exception.response
133
156
  err = Conjur::Error.create exception.response.body
134
157
  if err
135
158
  $stderr.puts "error: " + err.message
@@ -19,6 +19,7 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  require 'base64'
22
+ require 'semantic'
22
23
 
23
24
  module Conjur
24
25
  class Command
@@ -104,6 +105,17 @@ module Conjur
104
105
  command.switch [:a, :annotate]
105
106
  end
106
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
+
107
119
  def prompt_for_annotations
108
120
  highline.say('Add annotations (a name and value for each one):')
109
121
  {}.tap do |annotations|
@@ -254,6 +266,22 @@ an alternative destination role.)
254
266
  obj.role.revoke_from member
255
267
  end
256
268
  end
269
+
270
+ def retire_internal_role roleObj
271
+ members = roleObj.members
272
+ # Move the invoking role to the end of the roles list, so that it doesn't
273
+ # lose its permissions in the middle of this operation.
274
+ self_member = members.select{|m| m.member.roleid == current_user.role.roleid}
275
+ self_member.each do |m|
276
+ members.delete m
277
+ end
278
+ members.concat self_member if self_member
279
+ members.each do |r|
280
+ member = api.role(r.member)
281
+ puts "Revoking from role #{member.roleid}"
282
+ roleObj.revoke_from member
283
+ end
284
+ end
257
285
 
258
286
  def give_away_resource obj, options
259
287
  destination = options[:"destination-role"]
@@ -420,6 +448,14 @@ an alternative destination role.)
420
448
 
421
449
  password
422
450
  end
451
+
452
+ def has_admin?(role, other_role)
453
+ memberships = role.memberships.map(&:roleid)
454
+ other_role.members.any? { |m| memberships.member?(m.member.roleid) && m.admin_option }
455
+ rescue RestClient::Forbidden
456
+ false
457
+ end
458
+
423
459
  end
424
460
  end
425
461
  end
@@ -138,6 +138,18 @@ class Conjur::Command
138
138
  id = full_resource_id(require_arg args, "resource")
139
139
  api.audit_resource(id, options){|es| show_audit_events es, options}
140
140
  end
141
+
142
+ audit.desc "Send custom event(s) to audit system"
143
+ audit.long_desc "Send custom event(s) to audit system. Events should be provided in JSON format, describing either single hash or array of hashes."
144
+ audit.arg_name "( json_string | STDIN )"
145
+ audit.command :send do |c|
146
+ c.action do |global_options, options, args|
147
+ json = ( args.shift || STDIN.read )
148
+ api.audit_send json
149
+ puts "Events sent successfully"
150
+ end
151
+ end
152
+
141
153
  end
142
154
  end
143
155
  end
@@ -58,18 +58,14 @@ class Conjur::Command::Bootstrap < Conjur::Command
58
58
  api.user(username)
59
59
  end
60
60
  security_admin = api.group("security_admin")
61
- memberships = user.role.memberships.map(&:roleid) if user
62
61
 
63
62
  if user
63
+ memberships = user.role.memberships.map(&:roleid)
64
64
  if security_admin.exists?
65
- begin
66
- # The user has a role which is admin of the security_admin role
67
- # The user has the role which owns the security_admin resource
68
- security_admin.role.members.find{|m| memberships.member?(m.member.roleid) && m.admin_option} &&
69
- memberships.member?(security_admin.resource.ownerid)
70
- rescue RestClient::Forbidden
71
- false
72
- end
65
+ # The user has a role which is admin of the security_admin role
66
+ # The user has the role which owns the security_admin resource
67
+ has_admin?(user.role, security_admin.role) &&
68
+ memberships.member?(security_admin.resource.ownerid)
73
69
  else
74
70
  user.login == "admin"
75
71
  end
@@ -0,0 +1,187 @@
1
+ #
2
+ # Copyright (C) 2014 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
+
22
+ class Conjur::Command::HostFactories < Conjur::Command
23
+ desc "Manage host factories"
24
+
25
+ command :hostfactory do |hf|
26
+ hf.desc "Create a new host factory"
27
+ hf.arg_name "id"
28
+ hf.command :create do |c|
29
+ acting_as_option(c)
30
+
31
+ c.arg_name "layer"
32
+ c.desc "A space-delimited list of layers to which new hosts will belong"
33
+ c.flag [:l, :layer]
34
+
35
+ c.action do |global_options,options,args|
36
+ id = require_arg(args, 'hostfactory')
37
+
38
+ unless options[:ownerid]
39
+ exit_now! "Use --as-group or --as-role to indicate the host factory role"
40
+ end
41
+
42
+ owner_role = api.role(options[:ownerid])
43
+
44
+ layers = (options[:layer] || "").split(/\s/)
45
+ exit_now! "Provide at least one layer" unless layers.count > 0
46
+
47
+ layers.each do |layerid|
48
+ layer = api.layer(layerid)
49
+ exit_now! "Layer '#{layerid}' does not exist" unless layer.exists?
50
+ unless has_admin?(owner_role, layer.role)
51
+ exit_now! "#{owner_role.id} must be an admin of layer '#{layerid}' to create a host factory for it"
52
+ end
53
+ end
54
+
55
+ command_options = options.dup
56
+ command_options[:layers] = layers
57
+ command_options[:roleid] = options[:ownerid]
58
+
59
+ host_factory = api.create_host_factory id, command_options
60
+ display host_factory
61
+ end
62
+ end
63
+
64
+ hf.desc "Show a host factory"
65
+ hf.arg_name "id"
66
+ hf.command :show do |c|
67
+ c.action do |global_options,options,args|
68
+ id = require_arg(args, 'id')
69
+ display(api.host_factory(id), options)
70
+ end
71
+ end
72
+
73
+ hf.desc "List host factories"
74
+ hf.command :list do |c|
75
+ command_options_for_list c
76
+ c.action do |global_options, options, args|
77
+ command_impl_for_list global_options, options.merge(kind: "host_factory"), args
78
+ end
79
+ end
80
+
81
+ hf.desc "Operations on tokens"
82
+ hf.long_desc <<-DESC
83
+ This command creates one or more identical tokens. A token is always created with an
84
+ expiration time, which by default is 1 hour from now. The expiration time can be customized
85
+ with command arguments specifying the number of minutes, hours, days for which the token
86
+ will be valid.
87
+
88
+ By default, this command creates one token. Optionally, it can be used to create multiple identical tokens.
89
+ DESC
90
+ hf.command :tokens do |tokens|
91
+
92
+ tokens.desc "Create one or more tokens"
93
+ tokens.arg_name "hostfactory"
94
+ tokens.command :create do |c|
95
+ c.arg_name "duration in minutes"
96
+ c.desc "Number of minutes from now in which the token will expire"
97
+ c.flag [:"duration-minutes"]
98
+
99
+ c.arg_name "duration in hours"
100
+ c.desc "Number of hours from now in which the token will expire"
101
+ c.flag [:"duration-hours"]
102
+
103
+ c.arg_name "duration in days"
104
+ c.desc "Number of days from now in which the token will expire"
105
+ c.flag [:"duration-days"]
106
+
107
+ c.arg_name "count"
108
+ c.desc "Number of identical tokens to create"
109
+ c.flag [:c, :count]
110
+
111
+ c.arg_name "cidr"
112
+ c.desc "A comma-delimited list of CIDR addresses to restrict token to (optional)"
113
+ c.flag [:cidr]
114
+
115
+ c.action do |global_options,options,args|
116
+ id = require_arg(args, 'hostfactory')
117
+
118
+ duration = 0
119
+ %w(duration-minutes duration-hours duration-days).each do |d|
120
+ if t = options[d.to_sym]
121
+ duration += t.to_i.send(d.split('-')[-1])
122
+ end
123
+ end
124
+ if duration == 0
125
+ duration = 1.hour
126
+ end
127
+ expiration = Time.now + duration
128
+ count = (options[:count] || 1).to_i
129
+ command_options = {}
130
+
131
+ cidr = format_cidr(options.delete(:cidr))
132
+ command_options[:cidr] = cidr unless cidr.nil?
133
+
134
+ tokens = api.host_factory(id).create_tokens expiration, count, command_options
135
+ display tokens.map(&:to_json)
136
+ end
137
+ end
138
+
139
+ tokens.desc "Revoke (delete) a token"
140
+ tokens.arg_name "token"
141
+ tokens.command :revoke do |c|
142
+ c.action do |global_options,options,args|
143
+ token = require_arg(args, 'token')
144
+
145
+ api.revoke_host_factory_token token
146
+ puts "Token revoked"
147
+ end
148
+ end
149
+
150
+ tokens.desc "Show a token"
151
+ tokens.arg_name "token"
152
+ tokens.command :show do |c|
153
+ c.action do |global_options,options,args|
154
+ token = require_arg(args, 'token')
155
+
156
+ display api.show_host_factory_token(token), options
157
+ end
158
+ end
159
+ end
160
+
161
+ hf.desc "Operations on hosts"
162
+ hf.command :hosts do |hosts|
163
+ hosts.desc "Use a token to create a host"
164
+ hosts.arg_name "token host-id"
165
+ hosts.command :create do |c|
166
+ c.action do |global_options,options,args|
167
+ token = require_arg(args, 'token')
168
+ id = require_arg(args, 'host-id')
169
+
170
+ host = Conjur::API.host_factory_create_host token, id, options
171
+ display host
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ def self.format_cidr(cidr)
178
+ case cidr
179
+ when 'all'
180
+ []
181
+ when nil
182
+ nil
183
+ else
184
+ cidr.split(',').each {|x| x.strip!}
185
+ end
186
+ end
187
+ end