conjur-api 4.20.1 → 4.21.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 499e973eac8164648777759b0128f502ad692bb6
4
- data.tar.gz: 6d85fe429b8d77cb7aaca33370c0bc48179e5345
3
+ metadata.gz: d71200c2e70d47ac438ff0c37aefb9eb3336291a
4
+ data.tar.gz: 81b7029927b6227918d9449e5e279280194a56a1
5
5
  SHA512:
6
- metadata.gz: fa2d3a7024f3d41e6e87f81873179a1528b5d9b6d0a5657a8e8c281d8368189b3db301d3b525d88697d049d44868bac8df6b26aa026d2da48e785ed62d4ab843
7
- data.tar.gz: 301f2f79b84e3c8a3f13a2cdb21666d33e3cc80bc0e5144aa8974e19a248070d49c081d6ffaabc41c4b06925cbfce7b637a4b30a91b3aba36e94e12927a0c160
6
+ metadata.gz: 6f2dc2ffd7e4a1fe1b183e25c907dbb3e442e68e324993405913256ad2b1a6d44465485d30a686aa94db161ea302d0e15da84500c669b1824ad6623a04f7225b
7
+ data.tar.gz: c991fdd8ff7d36c2dbc3d74e03f469d9d235ced8b30735b3454991aa9ea53c3f56140b0981022823f2c092c72b2ec1336140956f2745ec5237ee9de0ac996dee
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ features/reports
1
2
  .DS_Store
2
3
  build_number
3
4
  *.gem
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # v4.21.0
2
+
3
+ * Add extensible Bootstrap commands as API methods.
4
+ * `bootstrap` grants `reveal` and `elevate` to the `security_admin` group.
5
+ * `bootstrap` creates `webservice:authn-tv`.
6
+ * `bootstrap` creates an `auditors` group and gives `reveal` privilege to it.
7
+
1
8
  # v4.20.1
2
9
 
3
10
  * BUGFIX: Better handling for unicode and special characters in user ids.
data/Rakefile CHANGED
@@ -1,26 +1,25 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
3
  require "yard"
4
+ require 'ci/reporter/rake/rspec'
5
+ require 'cucumber'
6
+ require 'cucumber/rake/task'
7
+ require 'rspec/core/rake_task'
4
8
 
5
- begin
6
- require 'rspec/core/rake_task'
7
- RSpec::Core::RakeTask.new(:spec) do |t|
8
- t.rspec_opts = '--order rand'
9
- end
10
- rescue LoadError
11
- $stderr.puts "RSpec Rake tasks not available in environment #{ENV['RACK_ENV']}"
12
- end
13
-
9
+ RSpec::Core::RakeTask.new :spec
10
+ Cucumber::Rake::Task.new :features
14
11
  YARD::Rake::YardocTask.new(:yard)
15
12
 
16
- task :jenkins do
13
+ task :jenkins => ['ci:setup:rspec', :spec] do
17
14
  if ENV['BUILD_NUMBER']
18
15
  File.write('build_number', ENV['BUILD_NUMBER'])
19
16
  end
20
- require 'ci/reporter/rake/rspec'
21
- Rake::Task["ci:setup:rspec"].invoke
22
- Rake::Task["spec"].invoke
17
+ require 'fileutils'
18
+ FileUtils.rm_rf 'features/reports'
19
+ Cucumber::Rake::Task.new do |t|
20
+ t.cucumber_opts = "--tags ~@real-api --format pretty --format junit --out features/reports"
21
+ end.runner.run
23
22
  Rake::Task["yard"].invoke
24
23
  end
25
24
 
26
- task default: :spec
25
+ task default: [:spec, :features]
data/ci/test.sh ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/bash -e
2
+
3
+ cd /src/conjur-api
4
+
5
+ export CONJUR_AUTHN_LOGIN=admin
6
+ export CONJUR_AUTHN_API_KEY=secret
7
+
8
+ bundle
9
+ bundle exec rake jenkins || true
data/conjur-api.gemspec CHANGED
@@ -28,6 +28,9 @@ Gem::Specification.new do |gem|
28
28
  gem.add_development_dependency 'rspec', '~> 3'
29
29
  gem.add_development_dependency 'rspec-expectations', '~> 3.4'
30
30
  gem.add_development_dependency 'webmock'
31
+ gem.add_development_dependency 'cucumber'
32
+ gem.add_development_dependency 'conjur-cli'
33
+ gem.add_development_dependency 'conjur-debify'
31
34
  gem.add_development_dependency 'ci_reporter_rspec'
32
35
  gem.add_development_dependency 'simplecov'
33
36
  gem.add_development_dependency 'io-grab'
@@ -0,0 +1,27 @@
1
+ Feature: conjur bootstrap
2
+
3
+ Background: Bootstrap
4
+ Given I bootstrap
5
+
6
+ Scenario: Expected resources exist
7
+ Then expressions "$conjur.group('security_admin').exists?" and "true" are equal
8
+ Then expressions "$conjur.group('auditors').exists?" and "true" are equal
9
+ Then expressions "$conjur.group('pubkeys-1.0/key-managers').exists?" and "true" are equal
10
+ Then expressions "$conjur.resource('webservice:conjur/authn-tv').exists?" and "true" are equal
11
+ Then expressions "$conjur.resource('webservice:conjur/policy-loader').exists?" and "true" are equal
12
+ Then expressions "$conjur.host('conjur/policy-loader').exists?" and "true" are equal
13
+ Then expressions "$conjur.host('conjur/secrets-rotator').exists?" and "true" are equal
14
+ Then expressions "$conjur.host('conjur/ldap-sync').exists?" and "true" are equal
15
+
16
+ Scenario: security_admin group has the expected members
17
+ Then expressions "$conjur.role('group:security_admin').members.map(&:member).map(&:roleid).sort.join(',')" and "'cucumber:host:conjur/authn-tv,cucumber:host:conjur/ldap-sync,cucumber:host:conjur/policy-loader,cucumber:host:conjur/secrets-rotator,cucumber:user:admin'" are equal
18
+
19
+ Scenario: security_admin can 'elevate' and 'reveal'
20
+ Then expression "$conjur.resource('!:!:conjur').permitted_roles('elevate')" includes "$conjur.group('security_admin').roleid"
21
+ Then expression "$conjur.resource('!:!:conjur').permitted_roles('reveal')" includes "$conjur.group('security_admin').roleid"
22
+
23
+ Scenario: auditors can 'reveal'
24
+ Then expression "$conjur.resource('!:!:conjur').permitted_roles('reveal')" includes "$conjur.group('auditors').roleid"
25
+
26
+ Scenario: API keys are saved in variables
27
+ Then expression "$conjur.resources(kind: 'variable').map(&:resourceid)" includes "'cucumber:variable:conjur/hosts/conjur/secrets-rotator/api-key'"
@@ -0,0 +1,24 @@
1
+ Then(/^I bootstrap$/) do
2
+ class Listener
3
+ attr_accessor :messages
4
+
5
+ def initialize
6
+ @messages = []
7
+ end
8
+
9
+ def echo msg
10
+ @messages.push msg
11
+ end
12
+ end
13
+ @listener = Listener.new
14
+
15
+ $conjur.bootstrap @listener
16
+ end
17
+
18
+ Then(/^expressions "([^"]*)" and "([^"]*)" are equal$/) do |code, test|
19
+ expect(eval(code)).to eq(eval(test))
20
+ end
21
+
22
+ Then(/^expression "([^"]*)" includes "([^"]*)"$/) do |code, test|
23
+ expect(eval(code)).to include(eval(test))
24
+ end
@@ -0,0 +1,5 @@
1
+ require 'conjur/cli'
2
+
3
+ Conjur::Config.load
4
+ Conjur::Config.apply
5
+ $conjur = Conjur::Authn.connect nil, noask: true
data/jenkins.sh CHANGED
@@ -1,11 +1,27 @@
1
- #!/bin/bash -e
1
+ #!/bin/bash -ex
2
2
 
3
- docker build -t api-ruby .
3
+ CONJUR_VERSION=${CONJUR_VERSION:-"4.6"}
4
+ DOCKER_IMAGE=${DOCKER_IMAGE:-"registry.tld/conjur-appliance-cuke-master:$CONJUR_VERSION-stable"}
5
+ NOKILL=${NOKILL:-"0"}
6
+ PULL=${PULL:-"1"}
4
7
 
5
- docker run --rm \
6
- -v $PWD:/src \
7
- api-ruby \
8
- bash -c '''
9
- bundle
10
- bundle exec rake jenkins
11
- '''
8
+ if [ -z "$CONJUR_CONTAINER" ]; then
9
+ if [ "$PULL" == "1" ]; then
10
+ docker pull $DOCKER_IMAGE
11
+ fi
12
+
13
+ cid=$(docker run -d -v ${PWD}:/src/conjur-api $DOCKER_IMAGE)
14
+ function finish {
15
+ if [ "$NOKILL" != "1" ]; then
16
+ docker rm -f ${cid}
17
+ fi
18
+ }
19
+ trap finish EXIT
20
+
21
+ >&2 echo "Container id:"
22
+ >&2 echo $cid
23
+ else
24
+ cid=${CONJUR_CONTAINER}
25
+ fi
26
+
27
+ docker exec -i ${cid} /src/conjur-api/ci/test.sh
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Conjur
21
21
  class API
22
- VERSION = "4.20.1"
22
+ VERSION = "4.21.0"
23
23
  end
24
24
  end
data/lib/conjur/api.rb CHANGED
@@ -42,6 +42,7 @@ require 'conjur/core-api'
42
42
  require 'conjur/layer-api'
43
43
  require 'conjur/pubkeys-api'
44
44
  require 'conjur/host-factory-api'
45
+ require 'conjur/bootstrap'
45
46
  require 'conjur-api/version'
46
47
  require 'conjur/api/info'
47
48
 
@@ -56,7 +56,7 @@ module Conjur
56
56
  log do |logger|
57
57
  logger << "Creating host_factory #{id}"
58
58
  unless options.blank?
59
- logger << " with options #{options.inspect}"
59
+ logger << " with options #{options.to_json}"
60
60
  end
61
61
  end
62
62
  options ||= {}
@@ -25,7 +25,7 @@ module Conjur
25
25
  class API
26
26
  class << self
27
27
  # @api private
28
- # TODO WTF does this do!
28
+ # deprecated
29
29
  def enroll_host(url)
30
30
  if Conjur.log
31
31
  Conjur.log << "Enrolling host with URL #{url}\n"
data/lib/conjur/base.rb CHANGED
@@ -186,6 +186,15 @@ module Conjur
186
186
  def username
187
187
  @username || @token['data']
188
188
  end
189
+
190
+ # Perform all commands in Conjur::Bootstrap::Command.
191
+ def bootstrap listener
192
+ Conjur::Bootstrap::Command.constants.map{|c| Conjur::Bootstrap::Command.const_get(c)}.each do |cls|
193
+ next unless cls.is_a?(Class)
194
+ next unless cls.superclass == Conjur::Bootstrap::Command::Base
195
+ cls.new(self, listener).perform
196
+ end
197
+ end
189
198
 
190
199
  # @api private
191
200
  # used to delegate to host providing subclasses.
@@ -0,0 +1,151 @@
1
+ module Conjur
2
+ module Bootstrap
3
+ module Command
4
+ Base = Struct.new(:api, :listener) do
5
+ def echo msg
6
+ listener.echo msg
7
+ end
8
+
9
+ def security_admin
10
+ api.group("security_admin")
11
+ end
12
+
13
+ def auditors
14
+ api.group("auditors")
15
+ end
16
+
17
+ def find_or_create_record record, owner = nil, &block
18
+ if record.exists?
19
+ echo "#{record.resource_kind.capitalize} '#{record.id}' already exists"
20
+ record
21
+ else
22
+ echo "Creating #{record.resource_kind} '#{record.id}'"
23
+ options = {}
24
+ options[:ownerid] = owner.roleid if owner
25
+ result = if block_given?
26
+ yield record, options
27
+ else
28
+ api.send "create_#{record.resource_kind}", record.id, options
29
+ end
30
+ store_api_key result if result.attributes['api_key']
31
+ result
32
+ end
33
+ end
34
+
35
+ def find_or_create_resource resource, owner = nil
36
+ if resource.exists?
37
+ echo "#{resource.resource_kind.capitalize} '#{resource.identifier}' already exists"
38
+ else
39
+ echo "Creating #{resource.resource_kind} '#{resource.identifier}'"
40
+ options = {}
41
+ options[:ownerid] = owner.roleid if owner
42
+ api.create_resource resource.resourceid, options
43
+ end
44
+ end
45
+
46
+ def store_api_key user
47
+ api.create_variable "text/plain",
48
+ "conjur-api-key",
49
+ id: "conjur/#{user.resource_kind.pluralize}/#{user.id}/api-key",
50
+ value: user.api_key,
51
+ ownerid: security_admin.role.roleid
52
+ echo "The API of #{user.resource_kind} #{user.id} is stored in variable 'conjur/#{user.resource_kind.pluralize}/#{user.id}/api-key'. " +
53
+ "You can retire the variable if you don't want to keep it there."
54
+ end
55
+
56
+ def permit resource, privilege, role
57
+ if resource.permitted_roles(privilege).member?(role.roleid)
58
+ echo "#{role.roleid} already has '#{privilege}' privilege on #{resource.resourceid}"
59
+ else
60
+ resource.permit privilege, role
61
+ end
62
+ end
63
+ end
64
+
65
+ class SecurityAdminGroup < Base
66
+ def perform
67
+ find_or_create_record security_admin
68
+
69
+ security_admin.resource.give_to(security_admin) unless security_admin.resource.ownerid == security_admin.role.roleid
70
+ end
71
+ end
72
+
73
+ class AuditorsGroup < Base
74
+ def perform
75
+ find_or_create_record auditors, security_admin
76
+ end
77
+ end
78
+
79
+ class Pubkeys < Base
80
+ def perform
81
+ find_or_create_record key_managers, security_admin
82
+ find_or_create_record pubkeys_layer, security_admin
83
+ find_or_create_record pubkeys_host, security_admin do |record, options|
84
+ api.create_host(id: record.id, ownerid: security_admin.roleid)
85
+ end
86
+ pubkeys_layer.add_host pubkeys_host unless pubkeys_layer.hosts.map(&:roleid).member?(pubkeys_host.roleid)
87
+
88
+ find_or_create_resource pubkeys_service, security_admin
89
+ permit pubkeys_service, 'update', key_managers
90
+ end
91
+
92
+ def pubkeys_layer
93
+ api.layer("pubkeys-1.0/public-keys")
94
+ end
95
+
96
+ def pubkeys_host
97
+ api.host("conjur/pubkeys")
98
+ end
99
+
100
+ def pubkeys_service
101
+ api.resource("service:pubkeys-1.0/public-keys")
102
+ end
103
+
104
+ def key_managers
105
+ api.group("pubkeys-1.0/key-managers")
106
+ end
107
+ end
108
+
109
+ class Attic < Base
110
+ def perform
111
+ find_or_create_record attic
112
+ end
113
+
114
+ def attic_user_name
115
+ "attic"
116
+ end
117
+
118
+ def attic
119
+ api.user(attic_user_name)
120
+ end
121
+ end
122
+
123
+ # Create a set of hosts that have security_admin privilege.
124
+ class SystemAccounts < Base
125
+ def perform
126
+ for hostname in %w(conjur/authn-tv conjur/secrets-rotator conjur/policy-loader conjur/ldap-sync)
127
+ find_or_create_resource api.resource("webservice:#{hostname}"), security_admin
128
+ find_or_create_record api.host(hostname), security_admin do |record, options|
129
+ api.create_host(id: record.id, ownerid: security_admin.roleid).tap do |host|
130
+ host.role.revoke_from security_admin
131
+ security_admin.add_member host
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ class GlobalPrivileges < Base
139
+ def perform
140
+ permit conjur_resource, 'elevate', security_admin
141
+ permit conjur_resource, 'reveal', security_admin
142
+ permit conjur_resource, 'reveal', auditors
143
+ end
144
+
145
+ def conjur_resource
146
+ api.resource("!:!:conjur")
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
data/lib/conjur/layer.rb CHANGED
@@ -77,7 +77,7 @@ module Conjur
77
77
  # @return [Array<Conjur::Host>] the hosts in the layer.
78
78
  def hosts
79
79
  self.attributes['hosts'].collect do |id|
80
- Conjur::Host.new(Conjur::API.core_asset_host, options)["hosts/#{fully_escape id}"]
80
+ Conjur::Host.new(Conjur::API.core_asset_host, options)["hosts/#{fully_escape id.split(':', 3)[-1]}"]
81
81
  end
82
82
  end
83
83
  end
@@ -34,6 +34,8 @@ module Conjur
34
34
  include PathBased
35
35
  include Exists
36
36
 
37
+ alias resource_kind kind
38
+
37
39
  # The identifier part of the `resource_id` for this resource. The identifier
38
40
  # is the resource id without the `account` and `kind` parts.
39
41
  #
@@ -45,7 +45,7 @@ module Conjur
45
45
  logger << "Creating #{type}"
46
46
  logger << " #{id}" if id
47
47
  unless options.blank?
48
- logger << " with options #{options.inspect}"
48
+ logger << " with options #{options.to_json}"
49
49
  end
50
50
  end
51
51
  options ||= {}
data/spec/ssl_spec.rb CHANGED
@@ -25,18 +25,20 @@ describe 'SSL connection' do
25
25
  expect { Conjur::API.login 'foo', 'bar' }.to raise_error RestClient::ResourceNotFound
26
26
  end
27
27
  end
28
-
29
- let(:port) { 54_128 }
28
+
29
+ let(:server) do
30
+ server = WEBrick::HTTPServer.new \
31
+ Port: 0, SSLEnable: true,
32
+ AccessLog: [], Logger: Logger.new('/dev/null'), # shut up, WEBrick
33
+ SSLCertificate: cert, SSLPrivateKey: key
34
+ end
35
+ let(:port) { server.config[:Port] }
30
36
 
31
37
  before do
32
38
  allow(Conjur::Authn::API).to receive(:host).and_return "https://localhost:#{port}"
33
39
  end
34
40
 
35
41
  around do |example|
36
- server = WEBrick::HTTPServer.new \
37
- Port: port, SSLEnable: true,
38
- AccessLog: [], Logger: Logger.new('/dev/null'), # shut up, WEBrick
39
- SSLCertificate: cert, SSLPrivateKey: key
40
42
  server_thread = Thread.new do
41
43
  server.start
42
44
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conjur-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.20.1
4
+ version: 4.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafal Rzepecki
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-18 00:00:00.000000000 Z
12
+ date: 2016-03-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
@@ -129,6 +129,48 @@ dependencies:
129
129
  - - '>='
130
130
  - !ruby/object:Gem::Version
131
131
  version: '0'
132
+ - !ruby/object:Gem::Dependency
133
+ name: cucumber
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ type: :development
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ - !ruby/object:Gem::Dependency
147
+ name: conjur-cli
148
+ requirement: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ type: :development
154
+ prerelease: false
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ - !ruby/object:Gem::Dependency
161
+ name: conjur-debify
162
+ requirement: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
132
174
  - !ruby/object:Gem::Dependency
133
175
  name: ci_reporter_rspec
134
176
  requirement: !ruby/object:Gem::Requirement
@@ -279,11 +321,11 @@ files:
279
321
  - LICENSE
280
322
  - README.md
281
323
  - Rakefile
324
+ - ci/test.sh
282
325
  - conjur-api.gemspec
283
- - features/enroll_server.feature
284
- - features/login.feature
285
- - features/ping_as_server.feature
286
- - features/ping_as_user.feature
326
+ - features/bootstrap.feature
327
+ - features/step_definitions/api_steps.rb
328
+ - features/support/env.rb
287
329
  - jenkins.sh
288
330
  - lib/conjur-api.rb
289
331
  - lib/conjur-api/version.rb
@@ -311,6 +353,7 @@ files:
311
353
  - lib/conjur/authn-api.rb
312
354
  - lib/conjur/authz-api.rb
313
355
  - lib/conjur/base.rb
356
+ - lib/conjur/bootstrap.rb
314
357
  - lib/conjur/build_from_response.rb
315
358
  - lib/conjur/cast.rb
316
359
  - lib/conjur/cert_utils.rb
@@ -416,10 +459,9 @@ signing_key:
416
459
  specification_version: 4
417
460
  summary: Conjur API
418
461
  test_files:
419
- - features/enroll_server.feature
420
- - features/login.feature
421
- - features/ping_as_server.feature
422
- - features/ping_as_user.feature
462
+ - features/bootstrap.feature
463
+ - features/step_definitions/api_steps.rb
464
+ - features/support/env.rb
423
465
  - spec/api/authn_spec.rb
424
466
  - spec/api/graph_spec.rb
425
467
  - spec/api/groups_spec.rb
@@ -1,26 +0,0 @@
1
- Feature: Server enrollment
2
-
3
- Background:
4
- When I login with my username and password
5
- And I receive an API key
6
-
7
- Scenario: Enroll this server
8
- When I enroll a server
9
- Then the request succeeds
10
- Then I receive an enrollment key
11
-
12
- Scenario: The server is granted no other roles by default
13
- Given I enroll a server
14
- And I switch to the server role
15
- When I list my roles
16
- Then the result contains 1 item
17
-
18
- Scenario: The server can be granted other roles
19
- Given I enroll a server
20
- And I create a role
21
- And I grant the role to the server
22
- And I switch to the server role
23
- And I list my roles
24
- Then the result contains 2 items
25
-
26
-
@@ -1,13 +0,0 @@
1
- Feature: Login
2
-
3
- Scenario: Login with my username and password
4
- When I login with my username and password
5
- Then the request succeeds
6
- And I receive an API key
7
-
8
- Scenario: Login with my username and password
9
- When I login with my username and invalid password
10
- Then the request fails with error 401
11
-
12
-
13
-
@@ -1,16 +0,0 @@
1
- Feature: Ping
2
-
3
- Background:
4
- Given I login with my username and password
5
- And I receive an API key
6
- And I enroll a server
7
- And I switch to the server role
8
-
9
- Scenario: Ping using the server enrollment key
10
- When I ping
11
- Then the request succeeds
12
-
13
- Scenario: Server credentials do not work from a different host
14
- Given I force the request IP address to 127.0.0.1
15
- When I ping
16
- Then the request fails with error 401
@@ -1,9 +0,0 @@
1
- Feature: Ping
2
-
3
- Background:
4
- When I login with my username and password
5
- And I receive an API key
6
-
7
- Scenario: Ping using an API key
8
- When I ping
9
- Then the request succeeds