conjur-api 2.5.1 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -18,4 +18,4 @@ test/version_tmp
18
18
  tmp
19
19
  .kateproject.d
20
20
  .rvmrc
21
-
21
+ .idea
@@ -25,4 +25,5 @@ Gem::Specification.new do |gem|
25
25
  gem.add_development_dependency 'webmock'
26
26
  gem.add_development_dependency 'ci_reporter'
27
27
  gem.add_development_dependency 'simplecov'
28
+ gem.add_development_dependency 'io-grab'
28
29
  end
@@ -1,6 +1,6 @@
1
1
  module Conjur
2
2
  class API
3
- VERSION = "2.5.1"
3
+ VERSION = "2.7.1"
4
4
  # Note: when bumping major version, please remove compatibility code in role#grant_to
5
5
  end
6
6
  end
@@ -27,6 +27,13 @@ module Conjur
27
27
  end
28
28
  JSON::parse(RestClient::Resource.new(Conjur::Authn::API.host)["users/#{fully_escape username}/authenticate"].post password, content_type: 'text/plain')
29
29
  end
30
+
31
+ def update_password username, password, new_password
32
+ if Conjur.log
33
+ Conjur.log << "Updating password for #{username}\n"
34
+ end
35
+ RestClient::Resource.new(Conjur::Authn::API.host, user: username, password: password)['users/password'].put new_password
36
+ end
30
37
  end
31
38
 
32
39
  # Options:
@@ -17,7 +17,16 @@ module Conjur
17
17
  end
18
18
 
19
19
  def role_from_username username
20
- role(username.split('/').unshift('user')[-2..-1].join(':'))
20
+ role(role_name_from_username username)
21
+ end
22
+
23
+ def role_name_from_username username = self.username
24
+ tokens = username.split('/')
25
+ if tokens.size == 1
26
+ [ 'user', username ].join(':')
27
+ else
28
+ [ tokens[0], tokens[1..-1].join('/') ].join(':')
29
+ end
21
30
  end
22
31
  end
23
32
  end
@@ -1,5 +1,6 @@
1
1
  require 'rest-client'
2
2
  require 'json'
3
+ require 'base64'
3
4
 
4
5
  require 'conjur/exists'
5
6
  require 'conjur/has_attributes'
@@ -1,4 +1,12 @@
1
1
  module Conjur
2
+ class API
3
+ class << self
4
+ def core_asset_host
5
+ ::Conjur::Core::API.host
6
+ end
7
+ end
8
+ end
9
+
2
10
  module Core
3
11
  class API < Conjur::API
4
12
  class << self
@@ -20,7 +20,7 @@ module Conjur
20
20
 
21
21
  def standard_list(host, type, options)
22
22
  JSON.parse(RestClient::Resource.new(host, credentials)[type.to_s.pluralize].get(options)).collect do |json|
23
- send(type, json['id']).tap do |obj|
23
+ send(type, fully_escape(json['id'])).tap do |obj|
24
24
  obj.attributes = json
25
25
  end
26
26
  end
@@ -13,8 +13,10 @@ describe Conjur::API do
13
13
  describe "::login" do
14
14
  it "gets /users/login" do
15
15
  RestClient::Request.should_receive(:execute).with(
16
- method: :get, url: "http://authn.example.com/users/login", user: user,
17
- password: password, headers: {}
16
+ method: :get, url: "http://authn.example.com/users/login",
17
+ user: user,
18
+ password: password,
19
+ headers: {}
18
20
  ).and_return(response = double)
19
21
  Conjur::API::login(user, password).should == response
20
22
  end
@@ -46,4 +48,18 @@ describe Conjur::API do
46
48
  Conjur::API.authenticate(user, password).should == { 'response' => 'foo' }
47
49
  end
48
50
  end
51
+
52
+ describe "::update_password" do
53
+ it "logs in and puts the new password" do
54
+ RestClient::Request.should_receive(:execute).with(
55
+ method: :put,
56
+ url: "http://authn.example.com/users/password",
57
+ user: user,
58
+ password: password,
59
+ payload: 'new-password',
60
+ headers: { }
61
+ ).and_return :response
62
+ Conjur::API.update_password(user, password, 'new-password').should == :response
63
+ end
64
+ end
49
65
  end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conjur::API, api: :dummy do
4
+ describe '#role_name_from_username' do
5
+ subject { api }
6
+ before {
7
+ api.stub(:username) { username }
8
+ }
9
+ context "username is" do
10
+ [
11
+ [ 'the-user', 'user:the-user' ],
12
+ [ 'host/the-host', 'host:the-host' ],
13
+ [ 'host/a/quite/long/host/name', 'host:a/quite/long/host/name' ],
14
+ [ 'newkind/host/name', 'newkind:host/name' ],
15
+ ].each do |p|
16
+ context "'#{p[0]}'" do
17
+ let(:username) { p[0] }
18
+ its("role_name_from_username") { should == p[1] }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'io_helper'
2
+ require 'io/grab'
3
3
  require 'tempfile'
4
4
 
5
5
  describe Conjur do
@@ -19,14 +19,14 @@ describe Conjur do
19
19
  context "with 'stdout'" do
20
20
  let(:param) { 'stdout' }
21
21
  it "creates something which writes to STDOUT" do
22
- STDOUT.grab { log << "foo" }.should == 'foo'
22
+ $stdout.grab { log << "foo" }.should == 'foo'
23
23
  end
24
24
  end
25
25
 
26
26
  context "with 'stderr'" do
27
27
  let(:param) { 'stderr' }
28
28
  it "creates something which writes to STDERR" do
29
- STDERR.grab { log << "foo" }.should == 'foo'
29
+ $stderr.grab { log << "foo" }.should == 'foo'
30
30
  end
31
31
  end
32
32
 
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe RestClient::Resource do
4
+ context "URL path parsing" do
5
+ let(:resource) { RestClient::Resource.new "http://test.host/#{path}" }
6
+
7
+ shared_examples_for "extracts the expected identifier" do
8
+ include Conjur::HasId
9
+ specify {
10
+ resource.path_components.should == path_components
11
+ id.should == path_components[2..-1].join('/')
12
+ }
13
+ end
14
+
15
+ it_should_behave_like "extracts the expected identifier" do
16
+ let(:path) { "hosts/foo" }
17
+ let(:path_components) { [ "", "hosts", "foo" ] }
18
+ end
19
+ it_should_behave_like "extracts the expected identifier" do
20
+ let(:path) { "hosts/foo/bar" }
21
+ let(:path_components) { [ "", "hosts", "foo", "bar" ] }
22
+ end
23
+ it_should_behave_like "extracts the expected identifier" do
24
+ let(:path) { "hosts/foo%2Fbar" }
25
+ let(:path_components) { [ "", "hosts", "foo/bar" ] }
26
+ end
27
+ end
28
+ 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: 2.5.1
4
+ version: 2.7.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-07-26 00:00:00.000000000 Z
13
+ date: 2013-10-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rest-client
@@ -140,6 +140,22 @@ dependencies:
140
140
  - - ! '>='
141
141
  - !ruby/object:Gem::Version
142
142
  version: '0'
143
+ - !ruby/object:Gem::Dependency
144
+ name: io-grab
145
+ requirement: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ! '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ type: :development
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ! '>='
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
143
159
  description: Conjur API
144
160
  email:
145
161
  - divided.mind@gmail.com
@@ -199,30 +215,15 @@ files:
199
215
  - lib/conjur/standard_methods.rb
200
216
  - lib/conjur/user.rb
201
217
  - lib/conjur/variable.rb
202
- - manual/asset/about.markdown
203
- - manual/asset/members.add.markdown
204
- - manual/asset/show.markdown
205
- - manual/group/about.markdown
206
- - manual/group/create.markdown
207
- - manual/host/about.markdown
208
- - manual/host/create.markdown
209
- - manual/host/enroll.markdown
210
- - manual/resource/about.markdown
211
- - manual/resource/create.markdown
212
- - manual/resource/deny.markdown
213
- - manual/resource/permit.markdown
214
- - manual/role/about.markdown
215
- - manual/role/members.markdown
216
- - manual/role/memberships.markdown
217
218
  - spec/api/authn_spec.rb
218
219
  - spec/api/groups_spec.rb
219
220
  - spec/api/hosts_spec.rb
220
221
  - spec/api/resources_spec.rb
222
+ - spec/api/roles_spec.rb
221
223
  - spec/api/secrets_spec.rb
222
224
  - spec/api/users_spec.rb
223
225
  - spec/api/variables_spec.rb
224
226
  - spec/cas_rest_client.rb
225
- - spec/io_helper.rb
226
227
  - spec/lib/api_spec.rb
227
228
  - spec/lib/build_from_response_spec.rb
228
229
  - spec/lib/exists_spec.rb
@@ -234,6 +235,7 @@ files:
234
235
  - spec/lib/role_spec.rb
235
236
  - spec/lib/standard_methods_spec.rb
236
237
  - spec/lib/user_spec.rb
238
+ - spec/rest_client/resource_spec.rb
237
239
  - spec/spec_helper.rb
238
240
  - spec/standard_methods_helper.rb
239
241
  - spec/variable_spec.rb
@@ -255,7 +257,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
255
257
  version: '0'
256
258
  segments:
257
259
  - 0
258
- hash: -863653852387038484
260
+ hash: -2598801054573023374
259
261
  required_rubygems_version: !ruby/object:Gem::Requirement
260
262
  none: false
261
263
  requirements:
@@ -264,7 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
264
266
  version: '0'
265
267
  segments:
266
268
  - 0
267
- hash: -863653852387038484
269
+ hash: -2598801054573023374
268
270
  requirements: []
269
271
  rubyforge_project:
270
272
  rubygems_version: 1.8.25
@@ -280,11 +282,11 @@ test_files:
280
282
  - spec/api/groups_spec.rb
281
283
  - spec/api/hosts_spec.rb
282
284
  - spec/api/resources_spec.rb
285
+ - spec/api/roles_spec.rb
283
286
  - spec/api/secrets_spec.rb
284
287
  - spec/api/users_spec.rb
285
288
  - spec/api/variables_spec.rb
286
289
  - spec/cas_rest_client.rb
287
- - spec/io_helper.rb
288
290
  - spec/lib/api_spec.rb
289
291
  - spec/lib/build_from_response_spec.rb
290
292
  - spec/lib/exists_spec.rb
@@ -296,6 +298,7 @@ test_files:
296
298
  - spec/lib/role_spec.rb
297
299
  - spec/lib/standard_methods_spec.rb
298
300
  - spec/lib/user_spec.rb
301
+ - spec/rest_client/resource_spec.rb
299
302
  - spec/spec_helper.rb
300
303
  - spec/standard_methods_helper.rb
301
304
  - spec/variable_spec.rb
@@ -1,12 +0,0 @@
1
- Asset
2
- =====
3
-
4
- Implements a domain-specific permission modeling concept. Assets combine functionality of roles, resources, and other assets
5
- together into unified APIs.
6
-
7
- Assets commonly perform the following functions:
8
-
9
- * **containment** An asset can contain other assets, such as an environment which contains variables or a deployment which contains hosts.
10
- Assets can be collected and re-combined arbitrarily.
11
- * **role management** Container assets can define roles which manage permissions on child assets.
12
-
@@ -1,52 +0,0 @@
1
- Asset#members#add
2
- =================
3
-
4
- A common purpose of an asset is to manage access to a collection of "child" assets. Permissions on child assets are granted
5
- via roles defined on the parent asset.
6
-
7
- For example, consider an Environment asset which contains a number of child Variables:
8
-
9
- ```
10
- Environment[e1]
11
- - variables
12
- - Variable[v1]
13
- - Variable[v2]
14
- - Variable[v3]
15
- ```
16
-
17
- The Environment may define a role "use_variable" and permit "read" and "execute" on each variable by the "use_variable" role.
18
- Therefore, granting the "use_variable" role to another role "r1" will permit r1 to read and execute each variable.
19
-
20
- Examples
21
- --------
22
-
23
- ### Command Line
24
-
25
- ```bash
26
- $ conjur asset:create environment `conjur id:create`
27
- {
28
- "id": "9yxa80",
29
- <snip>
30
- }
31
- $ conjur host:create
32
- {
33
- "id": "9bfqx5",
34
- <snip>
35
- }
36
- $ conjur environment:variables:create 9yxa80 test-var test text/plain "the-value"
37
- Variable created
38
- $ conjur asset:show environment 9yxa80
39
- {
40
- "id": "9yxa80",
41
- "variables": {
42
- "test-var": "he2e00"
43
- },
44
- <snip>
45
- }
46
- $ conjur resource:check variable he2e00 host:9bfqx5 execute
47
- false
48
- $ conjur asset:members:add environment 9yxa80 use_variable host:9bfqx5
49
- Membership granted
50
- $ conjur resource:check variable he2e00 host:9bfqx5 execute
51
- true
52
- ```
@@ -1,50 +0,0 @@
1
- Asset#show
2
- ==========
3
-
4
- Display an asset as JSON.
5
-
6
- The Conjur `jsonfield` utility can be used to pluck JSON values.
7
-
8
- Examples
9
- --------
10
-
11
- ### Command Line
12
-
13
- #### Create and show an environment
14
-
15
- ```bash
16
- $ id=`conjur id:create`
17
- $ e=`conjur asset:create environment $id`
18
- $ conjur asset:show environment id
19
- {
20
- "id": "0y3s00",
21
- "variables": {
22
- },
23
- "userid": "kgilpin",
24
- "ownerid": "sandbox:user:kgilpin",
25
- "resource_identifier": "sandbox:environment:0y3s00"
26
- }
27
- ```
28
- #### Create and show a host
29
-
30
- ```bash
31
- $ hostid=`conjur host:create | jsonfield id`
32
- $ conjur asset:show host $hostid
33
- {
34
- "id": "g7hytz",
35
- "userid": "kgilpin",
36
- "created_at": "2013-07-09T21:10:06+00:00",
37
- "ownerid": "sandbox:user:kgilpin",
38
- "roleid": "sandbox:host:g7hytz",
39
- "resource_identifier": "sandbox:host:g7hytz"
40
- }
41
- ```
42
-
43
- #### Attempt to show a non-existant asset.
44
-
45
- ```bash
46
- $ conjur asset:show host foo
47
- error: 404 Resource Not Found
48
- $ echo $?
49
- 1
50
- ```
@@ -1,6 +0,0 @@
1
- Group
2
- =====
3
-
4
- A group represents a collection of other roles (users, groups, hosts, generic roles, etc). Essentially
5
- it is just a role whose `kind` is "group".
6
-
@@ -1,20 +0,0 @@
1
- Group#create
2
- ============
3
-
4
- A group is created from an identifier. The identifier should be unique.
5
-
6
- Example
7
- -------
8
-
9
- ### Command Line
10
-
11
- ```bash
12
- $ conjur group:create `conjur id:create`
13
- Created https://core-sandbox-conjur.herokuapp.com/groups/5zjys0
14
- ```
15
-
16
- ### API
17
-
18
- ```ruby
19
- conjur.create_group SecureRandom.uuid
20
- ```
@@ -1,23 +0,0 @@
1
- Host
2
- ====
3
-
4
- A host represents a non-human user. VMs, processes, and jobs are all commonly represented as Hosts.
5
-
6
- Attributes
7
- ----------
8
-
9
- * **username** the host username is composed of the word 'host' composed with the host identifier. For example, "host/i-bbe231db"
10
- * **api_key** each host is assigned an API key, just like a human user
11
-
12
- Like a human User, a Host can authenticate with Conjur and perform actions.
13
-
14
- Roles
15
- -----
16
-
17
- On creation, the host creates a Conjur role to represent itself. The role kind is 'host'.
18
-
19
- Resources
20
- ---------
21
-
22
- On creation, the host creates a Conjur resource to represent itself.
23
-
@@ -1,34 +0,0 @@
1
- Host#create
2
- ===========
3
-
4
- A host is created with an identifier. The identifier must be unique.
5
- It can be assigned, or it can be generated by Conjur.
6
-
7
- When the host is created, the `api_key` field is included in the JSON response.
8
-
9
- Host#show does not include the `api_key`.
10
-
11
- Example
12
- -------
13
-
14
- ```bash
15
- $ conjur host:create
16
- {
17
- "id": "8y2p5w",
18
- "userid": "kgilpin",
19
- "created_at": "2013-07-09T21:12:57+00:00",
20
- "ownerid": "sandbox:user:kgilpin",
21
- "roleid": "sandbox:host:8y2p5w",
22
- "resource_identifier": "sandbox:host:8y2p5w",
23
- "api_key": "vj98ne10ar6vvmwrjt02ryfj6924a4vkch6yqcw292v9p12sv1rd"
24
- }
25
- $ conjur asset:show host 8y2p5w
26
- {
27
- "id": "8y2p5w",
28
- "userid": "kgilpin",
29
- "created_at": "2013-07-09T21:12:57+00:00",
30
- "ownerid": "sandbox:user:kgilpin",
31
- "roleid": "sandbox:host:8y2p5w",
32
- "resource_identifier": "sandbox:host:8y2p5w"
33
- }
34
- ```
@@ -1,21 +0,0 @@
1
- Host#enroll
2
- ===========
3
-
4
- Returns a unique URL which, when fetched, returns a shell command which will login
5
- the host with Conjur.
6
-
7
- Example
8
- -------
9
-
10
- ```bash
11
- $ conjur host:enroll vaz7qg
12
- https://core-sandbox-conjur.herokuapp.com/hosts/enroll?key=7fxt_HiRfOodA3J6wh5s2X9a6gm-b-wkPmF0g-HOPYo=
13
- On the target host, please execute the following command:
14
- curl -L https://core-sandbox-conjur.herokuapp.com/hosts/enroll?key=7fxt_HiRfOodA3J6wh5s2X9a6gm-b-wkPmF0g-HOPYo= | bash
15
- $ curl -L https://core-sandbox-conjur.herokuapp.com/hosts/enroll?key=7fxt_HiRfOodA3J6wh5s2X9a6gm-b-wkPmF0g-HOPYo=
16
- #!/bin/sh
17
- set -e
18
-
19
- conjur authn:login -u host/vaz7qg -p 39c63b3d5bc372de1c100feec852e05f747bc9eb
20
- ```
21
-
@@ -1,11 +0,0 @@
1
- Resource
2
- ========
3
-
4
- A resource is an entity on which permissions are assigned.
5
-
6
- Resources are partitioned into "kinds", such as "group", "host",
7
- "file", "environment", "variable", etc.
8
-
9
- Resources are not frequently used directly. Instead, higher-level "assets" such as Host, Group, User,
10
- Variable provide more intuitive functionality. For example, Assets often combine role and resource
11
- functionality together.
@@ -1,29 +0,0 @@
1
- Resource#create
2
- ===============
3
-
4
- A resource is composed of a `kind` and `identifier`. The `kind`
5
- indicates the semantic purpose of the resource, the identifier identifies
6
- it uniquely within the kind.
7
-
8
- Example
9
- -------
10
-
11
- ### Command Line
12
-
13
- ```bash
14
- $ conjur resource:create food bacon
15
- {
16
- "id": {
17
- "account": "sandbox",
18
- "kind": "food",
19
- "id": "bacon"
20
- },
21
- "owner": {
22
- "account": "sandbox",
23
- "id": "user:kgilpin"
24
- },
25
- "permissions": [
26
-
27
- ]
28
- }
29
- ```
@@ -1,23 +0,0 @@
1
- Resource#deny
2
- =============
3
-
4
- Removes a specific permission on a resource from a role.
5
-
6
- The owner of a resource always has all permissions on the resource, even if specific permissions
7
- are denied.
8
-
9
- Example
10
- -------
11
-
12
- ### Command Line
13
-
14
- ```bash
15
- $ conjur resource:permit food bacon host:a4yta8 fry
16
- Permission granted
17
- $ conjur resource:check food bacon host:a4yta8 fry
18
- true
19
- $ conjur resource:deny food bacon host:a4yta8 fry
20
- Permission revoked
21
- $ conjur resource:check food bacon host:a4yta8 fry
22
- false
23
- ```
@@ -1,35 +0,0 @@
1
- Resource#permit
2
- ===============
3
-
4
- Gives a specific permission on a resource to a role.
5
-
6
- Example
7
- -------
8
-
9
- ### Command Line
10
-
11
- ```bash
12
- $ conjur host:create
13
- <snip>
14
- $ conjur resource:permit food bacon host:a4yta8 fry
15
- Permission granted
16
- $ conjur resource:check food bacon host:a4yta8 fry
17
- true
18
- $ conjur resource:check food bacon host:a4yta8 bake
19
- false
20
- $ conjur resource:permitted_roles food bacon fry
21
- [
22
- {
23
- "id": {
24
- "account": "sandbox",
25
- "id": "user:kgilpin"
26
- }
27
- },
28
- {
29
- "id": {
30
- "account": "sandbox",
31
- "id": "host:a4yta8"
32
- }
33
- }
34
- ]
35
- ```
@@ -1,10 +0,0 @@
1
- Role
2
- ====
3
-
4
- A role is a target to which permissions are assigned. Permitting an action on a resource by a role enables the role to perform
5
- the specified action.
6
-
7
- In addition, roles can be "granted to" other roles. When role "A" is granted to role "B", role "B" gains the ability to perform
8
- all the actions permitted to "A". In addition, a role can be granted with "grant option". When role "A" is granted to role "B" with
9
- grant option, role "B" can in turn grant role "A" to other roles.
10
-
@@ -1,40 +0,0 @@
1
- Role#members
2
- ============
3
-
4
- Lists the roles that have been the recipient of a role grant. The creator of the role is always a role member.
5
-
6
- If role "A" is created by user "U" and then granted to roles "B" and "C", then the members of role "A" are [ "U", "B", "C" ].
7
-
8
- Examples
9
- --------
10
-
11
- ### Command Line
12
-
13
- #### Add a role member and list the members
14
-
15
- ```bash
16
- $ conjur role:create test:`conjur id:create`
17
- Created https://authz-v3-conjur.herokuapp.com/sandbox/roles/test/2y1300
18
- $ conjur role:members test:2y1300
19
- [
20
- "sandbox:user:kgilpin"
21
- ]
22
- $ conjur host:create
23
- {
24
- "id": "w43699",
25
- <snip>
26
- }
27
- $ conjur role:grant_to test:2y1300 host:w43699
28
- Role granted
29
- $ conjur role:members test:2y1300
30
- [
31
- "sandbox:user:kgilpin",
32
- "sandbox:host:w43699"
33
- ]
34
- ```
35
-
36
- #### The role argument is optional and defaults to the current role
37
-
38
- ```bash
39
- $ conjur role:members
40
- ```
@@ -1,26 +0,0 @@
1
- Role#memberships
2
- ================
3
-
4
- List the roles that a role has been granted, transitively.
5
-
6
- If role "A" is granted to role "B" which is granted to role "C", then the memberships of role "C" are [ "C", "B", "A" ].
7
-
8
- Examples
9
- --------
10
-
11
- ### Command Line
12
-
13
- ```bash
14
- $ ns=`conjur id:create`
15
- $ conjur role:create test:$ns/A
16
- $ conjur role:create test:$ns/B
17
- $ conjur role:create test:$ns/C
18
- $ conjur role:grant_to test:$ns/A test:$ns/B
19
- $ conjur role:grant_to test:$ns/B test:$ns/C
20
- $ conjur role:memberships test:$ns/C
21
- [
22
- "sandbox:test:dy7mg0/C",
23
- "sandbox:test:dy7mg0/B",
24
- "sandbox:test:dy7mg0/A"
25
- ]
26
- ```
@@ -1,18 +0,0 @@
1
- class IO
2
- def grab &block
3
- @grabbed_output = ""
4
- class << self
5
- def write arg
6
- @grabbed_output += arg
7
- end
8
- end
9
-
10
- begin
11
- yield
12
- ensure
13
- singleton_class.send :remove_method, :write
14
- end
15
-
16
- @grabbed_output
17
- end
18
- end