conjur-cli 4.28.2 → 4.29.0

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