cf-uaa-lib 3.6.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,255 +23,250 @@ describe TokenIssuer do
23
23
 
24
24
  before do
25
25
  #Util.default_logger(:trace)
26
- @issuer = TokenIssuer.new("http://test.uaa.target", "test_client", "test_secret", options)
26
+ @issuer = TokenIssuer.new('http://test.uaa.target', 'test_client', 'test_secret', options)
27
27
  end
28
28
 
29
29
  subject { @issuer }
30
30
 
31
- describe "initialize" do
31
+ describe 'initialize' do
32
32
  let(:options) { {:http_proxy => 'http-proxy.com', :https_proxy => 'https-proxy.com', :skip_ssl_validation => true} }
33
33
 
34
- it "sets proxy information" do
35
- subject.http_proxy.should == 'http-proxy.com'
36
- subject.https_proxy.should == 'https-proxy.com'
37
- end
38
-
39
- it "sets skip_ssl_validation" do
34
+ it 'sets skip_ssl_validation' do
40
35
  subject.skip_ssl_validation == true
41
36
  end
42
37
  end
43
38
 
44
- context "with client credentials grant" do
39
+ context 'with client credentials grant' do
45
40
 
46
- it "gets a token with client credentials" do
41
+ it 'gets a token with client credentials' do
47
42
  subject.set_request_handler do |url, method, body, headers|
48
- headers["content-type"].should =~ /application\/x-www-form-urlencoded/
49
- headers["accept"].should =~ /application\/json/
43
+ headers['content-type'].should =~ /application\/x-www-form-urlencoded/
44
+ headers['accept'].should =~ /application\/json/
50
45
  # TODO check basic auth header
51
- url.should == "http://test.uaa.target/oauth/token"
46
+ url.should == 'http://test.uaa.target/oauth/token'
52
47
  method.should == :post
53
- reply = {:access_token => "test_access_token", :token_type => "BEARER",
54
- :scope => "logs.read", :expires_in => 98765}
55
- [200, Util.json(reply), {"content-type" => "application/json"}]
48
+ reply = {:access_token => 'test_access_token', :token_type => 'BEARER',
49
+ :scope => 'logs.read', :expires_in => 98765}
50
+ [200, Util.json(reply), {'content-type' => 'application/json'}]
56
51
  end
57
- token = subject.client_credentials_grant("logs.read")
52
+ token = subject.client_credentials_grant('logs.read')
58
53
  token.should be_an_instance_of TokenInfo
59
- token.info["access_token"].should == "test_access_token"
60
- token.info["token_type"].should =~ /^bearer$/i
61
- token.info["scope"].should == "logs.read"
62
- token.info["expires_in"].should == 98765
54
+ token.info['access_token'].should == 'test_access_token'
55
+ token.info['token_type'].should =~ /^bearer$/i
56
+ token.info['scope'].should == 'logs.read'
57
+ token.info['expires_in'].should == 98765
63
58
  end
64
59
 
65
- it "gets all granted scopes if none specified" do
60
+ it 'gets all granted scopes if none specified' do
66
61
  subject.set_request_handler do |url, method, body, headers|
67
- reply = {:access_token => "test_access_token", :token_type => "BEARER",
68
- :scope => "openid logs.read", :expires_in => 98765}
69
- [200, Util.json(reply), {"content-type" => "application/json"}]
62
+ reply = {:access_token => 'test_access_token', :token_type => 'BEARER',
63
+ :scope => 'openid logs.read', :expires_in => 98765}
64
+ [200, Util.json(reply), {'content-type' => 'application/json'}]
70
65
  end
71
66
  token = subject.client_credentials_grant
72
- Util.arglist(token.info["scope"]).to_set.should == Util.arglist("openid logs.read").to_set
67
+ Util.arglist(token.info['scope']).to_set.should == Util.arglist('openid logs.read').to_set
73
68
  end
74
69
 
75
- it "raises a bad response error if response content type is not json" do
76
- subject.set_request_handler { [200, "not json", {"content-type" => "text/html"}] }
70
+ it 'raises a bad response error if response content type is not json' do
71
+ subject.set_request_handler { [200, 'not json', {'content-type' => 'text/html'}] }
77
72
  expect {subject.client_credentials_grant}.to raise_exception BadResponse
78
73
  end
79
74
 
80
- it "raises a bad response error if the response is not proper json" do
81
- subject.set_request_handler { [200, "bad json", {"content-type" => "application/json"}] }
75
+ it 'raises a bad response error if the response is not proper json' do
76
+ subject.set_request_handler { [200, 'bad json', {'content-type' => 'application/json'}] }
82
77
  expect {subject.client_credentials_grant}.to raise_exception BadResponse
83
78
  end
84
79
 
85
- it "raises a target error if the response is 400 with valid oauth json error" do
86
- subject.set_request_handler { [400, '{"error":"invalid scope"}', {"content-type" => "application/json"}] }
87
- expect {subject.client_credentials_grant("bad.scope")}.to raise_exception TargetError
80
+ it 'raises a target error if the response is 400 with valid oauth json error' do
81
+ subject.set_request_handler { [400, '{"error":"invalid scope"}', {'content-type' => 'application/json'}] }
82
+ expect {subject.client_credentials_grant('bad.scope')}.to raise_exception TargetError
88
83
  end
89
84
  end
90
85
 
91
- context "with owner password grant" do
86
+ context 'with owner password grant' do
92
87
 
93
- it "gets a token with owner password" do
88
+ it 'gets a token with owner password' do
94
89
  subject.set_request_handler do |url, method, body, headers|
95
- headers["content-type"].should =~ /application\/x-www-form-urlencoded/
96
- headers["accept"].should =~ /application\/json/
90
+ headers['content-type'].should =~ /application\/x-www-form-urlencoded/
91
+ headers['accept'].should =~ /application\/json/
97
92
  # TODO check basic auth header
98
- url.should == "http://test.uaa.target/oauth/token"
93
+ url.should == 'http://test.uaa.target/oauth/token'
99
94
  method.should == :post
100
- reply = {:access_token => "test_access_token", :token_type => "BEARER",
101
- :scope => "openid", :expires_in => 98765}
102
- [200, Util.json(reply), {"content-type" => "application/json"}]
95
+ reply = {:access_token => 'test_access_token', :token_type => 'BEARER',
96
+ :scope => 'openid', :expires_in => 98765}
97
+ [200, Util.json(reply), {'content-type' => 'application/json'}]
103
98
  end
104
- token = subject.owner_password_grant("joe+admin", "?joe's%password$@ ", "openid")
99
+ token = subject.owner_password_grant('joe+admin', "?joe's%password$@ ", 'openid')
105
100
  token.should be_an_instance_of TokenInfo
106
- token.info["access_token"].should == "test_access_token"
107
- token.info["token_type"].should =~ /^bearer$/i
108
- token.info["scope"].should == "openid"
109
- token.info["expires_in"].should == 98765
101
+ token.info['access_token'].should == 'test_access_token'
102
+ token.info['token_type'].should =~ /^bearer$/i
103
+ token.info['scope'].should == 'openid'
104
+ token.info['expires_in'].should == 98765
110
105
  end
111
106
 
112
- it "gets a token with passcode" do
107
+ it 'gets a token with passcode' do
113
108
  subject.set_request_handler do |url, method, body, headers|
114
- headers["content-type"].should =~ /application\/x-www-form-urlencoded/
115
- headers["accept"].should =~ /application\/json/
109
+ headers['content-type'].should =~ /application\/x-www-form-urlencoded/
110
+ headers['accept'].should =~ /application\/json/
116
111
  # TODO check basic auth header
117
- url.should == "http://test.uaa.target/oauth/token"
112
+ url.should == 'http://test.uaa.target/oauth/token'
118
113
  body.should =~ /(^|&)passcode=12345($|&)/
119
114
  body.should =~ /(^|&)grant_type=password($|&)/
120
115
  method.should == :post
121
- reply = {:access_token => "test_access_token", :token_type => "BEARER",
122
- :scope => "openid", :expires_in => 98765}
123
- [200, Util.json(reply), {"content-type" => "application/json"}]
116
+ reply = {:access_token => 'test_access_token', :token_type => 'BEARER',
117
+ :scope => 'openid', :expires_in => 98765}
118
+ [200, Util.json(reply), {'content-type' => 'application/json'}]
124
119
  end
125
- token = subject.passcode_grant("12345")
120
+ token = subject.passcode_grant('12345')
126
121
  token.should be_an_instance_of TokenInfo
127
- token.info["access_token"].should == "test_access_token"
128
- token.info["token_type"].should =~ /^bearer$/i
129
- token.info["scope"].should == "openid"
130
- token.info["expires_in"].should == 98765
122
+ token.info['access_token'].should == 'test_access_token'
123
+ token.info['token_type'].should =~ /^bearer$/i
124
+ token.info['scope'].should == 'openid'
125
+ token.info['expires_in'].should == 98765
131
126
  end
132
127
 
133
128
  end
134
129
 
135
- describe "#owner_password_credentials_grant" do
136
- it "gets a token grant type password" do
130
+ describe '#owner_password_credentials_grant' do
131
+ it 'gets a token grant type password' do
137
132
  subject.set_request_handler do |url, method, body, headers|
138
- headers["content-type"].should =~ /application\/x-www-form-urlencoded/
139
- headers["accept"].should =~ /application\/json/
140
- url.should == "http://test.uaa.target/oauth/token"
133
+ headers['content-type'].should =~ /application\/x-www-form-urlencoded/
134
+ headers['accept'].should =~ /application\/json/
135
+ url.should == 'http://test.uaa.target/oauth/token'
141
136
  method.should == :post
142
- body.split('&').should =~ ["passcode=fake-passcode", "grant_type=password"]
143
- reply = {:access_token => "test_access_token", :token_type => "BEARER",
144
- :scope => "openid", :expires_in => 98765}
145
- [200, Util.json(reply), {"content-type" => "application/json"}]
137
+ body.split('&').should =~ ['passcode=fake-passcode', 'grant_type=password']
138
+ reply = {:access_token => 'test_access_token', :token_type => 'BEARER',
139
+ :scope => 'openid', :expires_in => 98765}
140
+ [200, Util.json(reply), {'content-type' => 'application/json'}]
146
141
  end
147
- token = subject.owner_password_credentials_grant({passcode: "fake-passcode"})
142
+ token = subject.owner_password_credentials_grant({passcode: 'fake-passcode'})
148
143
  token.should be_an_instance_of TokenInfo
149
- token.info["access_token"].should == "test_access_token"
150
- token.info["token_type"].should =~ /^bearer$/i
151
- token.info["scope"].should == "openid"
152
- token.info["expires_in"].should == 98765
144
+ token.info['access_token'].should == 'test_access_token'
145
+ token.info['token_type'].should =~ /^bearer$/i
146
+ token.info['scope'].should == 'openid'
147
+ token.info['expires_in'].should == 98765
153
148
  end
154
149
 
155
150
  end
156
151
 
157
- context "with implicit grant" do
152
+ context 'with implicit grant' do
158
153
 
159
- it "gets the prompts for credentials used to authenticate implicit grant" do
154
+ it 'gets the prompts for credentials used to authenticate implicit grant' do
160
155
  subject.set_request_handler do |url, method, body, headers|
161
- info = { :prompts => {:username => ["text", "Username"], :password => ["password","Password"]} }
162
- [200, Util.json(info), {"content-type" => "application/json"}]
156
+ info = { :prompts => {:username => ['text', 'Username'], :password => ['password', 'Password']} }
157
+ [200, Util.json(info), {'content-type' => 'application/json'}]
163
158
  end
164
159
  result = subject.prompts
165
160
  result.should_not be_empty
166
161
  end
167
162
 
168
- it "raises a bad target error if no prompts are received" do
163
+ it 'raises a bad target error if no prompts are received' do
169
164
  subject.set_request_handler do |url, method, body, headers|
170
- [200, Util.json({}), {"content-type" => "application/json"}]
165
+ [200, Util.json({}), {'content-type' => 'application/json'}]
171
166
  end
172
167
  expect { subject.prompts }.to raise_exception BadResponse
173
168
  end
174
169
 
175
- context "#implicit_grant_with_creds" do
176
- it "gets only an access token, no openid in scope" do
170
+ context '#implicit_grant_with_creds' do
171
+ it 'gets only an access token, no openid in scope' do
177
172
  subject.set_request_handler do |url, method, body, headers|
178
- headers["content-type"].should =~ /application\/x-www-form-urlencoded/
179
- headers["accept"].should =~ /application\/json/
180
- url.should match "http://test.uaa.target/oauth/authorize"
173
+ headers['content-type'].should =~ /application\/x-www-form-urlencoded/
174
+ headers['accept'].should =~ /application\/json/
175
+ url.should match 'http://test.uaa.target/oauth/authorize'
181
176
  (state = /state=([^&]+)/.match(url)[1]).should_not be_nil
182
177
  method.should == :post
183
- location = "https://uaa.cloudfoundry.com/redirect/test_client#" +
184
- "access_token=test_access_token&token_type=bearer&" +
178
+ location = 'https://uaa.cloudfoundry.com/redirect/test_client#' +
179
+ 'access_token=test_access_token&token_type=bearer&' +
185
180
  "expires_in=98765&scope=logs.read&state=#{state}"
186
- [302, nil, {"content-type" => "application/json", "location" => location}]
181
+ [302, nil, {'content-type' => 'application/json', 'location' => location}]
187
182
  end
188
183
 
189
- expect(subject).to receive(:authorize_path_args).with("token", "https://uaa.cloudfoundry.com/redirect/test_client", "logs.read", anything)
190
- subject.stub(:random_state).and_return("1234")
191
- subject.stub(:authorize_path_args).and_return("/oauth/authorize?state=1234&scope=logs.read")
184
+ expect(subject).to receive(:authorize_path_args).with('token', 'https://uaa.cloudfoundry.com/redirect/test_client', 'logs.read', anything)
185
+ subject.stub(:random_state).and_return('1234')
186
+ subject.stub(:authorize_path_args).and_return('/oauth/authorize?state=1234&scope=logs.read')
192
187
 
193
- token = subject.implicit_grant_with_creds({:username => "joe+admin", :password => "?joe's%password$@ "}, "logs.read")
188
+ token = subject.implicit_grant_with_creds({:username => 'joe+admin', :password => "?joe's%password$@ "}, 'logs.read')
194
189
  token.should be_an_instance_of TokenInfo
195
- token.info["access_token"].should == "test_access_token"
196
- token.info["token_type"].should =~ /^bearer$/i
197
- Util.arglist(token.info["scope"]).to_set.should == Util.arglist("logs.read").to_set
198
- token.info["expires_in"].should == 98765
190
+ token.info['access_token'].should == 'test_access_token'
191
+ token.info['token_type'].should =~ /^bearer$/i
192
+ Util.arglist(token.info['scope']).to_set.should == Util.arglist('logs.read').to_set
193
+ token.info['expires_in'].should == 98765
199
194
  end
200
195
 
201
- it "also asks for an id_token if scope contains openid" do
196
+ it 'also asks for an id_token if scope contains openid' do
202
197
  subject.set_request_handler do |url, method, body, headers|
203
- location = "https://uaa.cloudfoundry.com/redirect/test_client#" +
204
- "access_token=test_access_token&id_token=test-id_token&token_type=bearer&" +
205
- "expires_in=98765&scope=openid+logs.read&state=1234"
206
- [302, nil, {"content-type" => "application/json", "location" => location}]
198
+ location = 'https://uaa.cloudfoundry.com/redirect/test_client#' +
199
+ 'access_token=test_access_token&id_token=test-id_token&token_type=bearer&' +
200
+ 'expires_in=98765&scope=openid+logs.read&state=1234'
201
+ [302, nil, {'content-type' => 'application/json', 'location' => location}]
207
202
  end
208
203
 
209
- expect(subject).to receive(:authorize_path_args).with("token id_token", "https://uaa.cloudfoundry.com/redirect/test_client", "openid logs.read", anything)
210
- subject.stub(:random_state).and_return("1234")
211
- subject.implicit_grant_with_creds({:username => "joe+admin", :password => "?joe's%password$@ "}, "openid logs.read")
204
+ expect(subject).to receive(:authorize_path_args).with('token id_token', 'https://uaa.cloudfoundry.com/redirect/test_client', 'openid logs.read', anything)
205
+ subject.stub(:random_state).and_return('1234')
206
+ subject.implicit_grant_with_creds({:username => 'joe+admin', :password => "?joe's%password$@ "}, 'openid logs.read')
212
207
  end
213
208
  end
214
209
 
215
- it "rejects an access token with wrong state" do
210
+ it 'rejects an access token with wrong state' do
216
211
  subject.set_request_handler do |url, method, body, headers|
217
- location = "https://uaa.cloudfoundry.com/redirect/test_client#" +
218
- "access_token=test_access_token&token_type=bearer&" +
219
- "expires_in=98765&scope=openid+logs.read&state=bad_state"
220
- [302, nil, {"content-type" => "application/json", "location" => location}]
212
+ location = 'https://uaa.cloudfoundry.com/redirect/test_client#' +
213
+ 'access_token=test_access_token&token_type=bearer&' +
214
+ 'expires_in=98765&scope=openid+logs.read&state=bad_state'
215
+ [302, nil, {'content-type' => 'application/json', 'location' => location}]
221
216
  end
222
- expect {token = subject.implicit_grant_with_creds(:username => "joe+admin",
217
+ expect {token = subject.implicit_grant_with_creds(:username => 'joe+admin',
223
218
  :password => "?joe's%password$@ ")}.to raise_exception BadResponse
224
219
  end
225
220
 
226
- it "asks for an id_token with openid scope" do
227
- uri_parts = subject.implicit_uri("http://call.back/uri_path", "openid logs.read").split('?')
221
+ it 'asks for an id_token with openid scope' do
222
+ uri_parts = subject.implicit_uri('http://call.back/uri_path', 'openid logs.read').split('?')
228
223
  params = Util.decode_form(uri_parts[1])
229
- params["response_type"].should == "token id_token"
224
+ params['response_type'].should == 'token id_token'
230
225
  end
231
226
 
232
227
  it "only asks for token if scope isn't openid" do
233
- uri_parts = subject.implicit_uri("http://call.back/uri_path").split('?')
228
+ uri_parts = subject.implicit_uri('http://call.back/uri_path').split('?')
234
229
  params = Util.decode_form(uri_parts[1])
235
- params["response_type"].should == "token"
230
+ params['response_type'].should == 'token'
236
231
  end
237
232
 
238
233
  end
239
234
 
240
- context "with auth code grant" do
235
+ context 'with auth code grant' do
241
236
 
242
- it "gets the authcode uri to be sent to the user agent for an authcode" do
243
- redir_uri = "http://call.back/uri_path"
244
- uri_parts = subject.authcode_uri(redir_uri, "openid").split('?')
245
- uri_parts[0].should == "http://test.uaa.target/oauth/authorize"
237
+ it 'gets the authcode uri to be sent to the user agent for an authcode' do
238
+ redir_uri = 'http://call.back/uri_path'
239
+ uri_parts = subject.authcode_uri(redir_uri, 'openid').split('?')
240
+ uri_parts[0].should == 'http://test.uaa.target/oauth/authorize'
246
241
  params = Util.decode_form(uri_parts[1])
247
- params["response_type"].should == "code"
248
- params["client_id"].should == "test_client"
249
- params["scope"].should == "openid"
250
- params["redirect_uri"].should == redir_uri
251
- params["state"].should_not be_nil
242
+ params['response_type'].should == 'code'
243
+ params['client_id'].should == 'test_client'
244
+ params['scope'].should == 'openid'
245
+ params['redirect_uri'].should == redir_uri
246
+ params['state'].should_not be_nil
252
247
  end
253
248
 
254
- it "gets an access token with an authorization code" do
249
+ it 'gets an access token with an authorization code' do
255
250
  subject.set_request_handler do |url, method, body, headers|
256
- headers["content-type"].should =~ /application\/x-www-form-urlencoded/
257
- headers["accept"].should =~ /application\/json/
251
+ headers['content-type'].should =~ /application\/x-www-form-urlencoded/
252
+ headers['accept'].should =~ /application\/json/
258
253
  # TODO check basic auth header
259
- url.should match "http://test.uaa.target/oauth/token"
254
+ url.should match 'http://test.uaa.target/oauth/token'
260
255
  method.should == :post
261
- reply = {:access_token => "test_access_token", :token_type => "BEARER",
262
- :scope => "openid", :expires_in => 98765}
263
- [200, Util.json(reply), {"content-type" => "application/json"}]
256
+ reply = {:access_token => 'test_access_token', :token_type => 'BEARER',
257
+ :scope => 'openid', :expires_in => 98765}
258
+ [200, Util.json(reply), {'content-type' => 'application/json'}]
264
259
  end
265
- cburi = "http://call.back/uri_path"
260
+ cburi = 'http://call.back/uri_path'
266
261
  redir_uri = subject.authcode_uri(cburi)
267
262
  state = /state=([^&]+)/.match(redir_uri)[1]
268
263
  reply_query = "state=#{state}&code=kz8%2F5gQZ2pc%3D"
269
264
  token = subject.authcode_grant(redir_uri, reply_query)
270
265
  token.should be_an_instance_of TokenInfo
271
- token.info["access_token"].should == "test_access_token"
272
- token.info["token_type"].should =~ /^bearer$/i
273
- token.info["scope"].should == "openid"
274
- token.info["expires_in"].should == 98765
266
+ token.info['access_token'].should == 'test_access_token'
267
+ token.info['token_type'].should =~ /^bearer$/i
268
+ token.info['scope'].should == 'openid'
269
+ token.info['expires_in'].should == 98765
275
270
  end
276
271
 
277
272
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cf-uaa-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Syer
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2016-08-11 00:00:00.000000000 Z
15
+ date: 2016-12-15 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: multi_json
@@ -28,6 +28,20 @@ dependencies:
28
28
  - - ">="
29
29
  - !ruby/object:Gem::Version
30
30
  version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: httpclient
33
+ requirement: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - "~>"
36
+ - !ruby/object:Gem::Version
37
+ version: 2.8.2.4
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - "~>"
43
+ - !ruby/object:Gem::Version
44
+ version: 2.8.2.4
31
45
  - !ruby/object:Gem::Dependency
32
46
  name: bundler
33
47
  requirement: !ruby/object:Gem::Requirement
@@ -154,7 +168,6 @@ files:
154
168
  - lib/uaa.rb
155
169
  - lib/uaa/http.rb
156
170
  - lib/uaa/info.rb
157
- - lib/uaa/proxy_options.rb
158
171
  - lib/uaa/scim.rb
159
172
  - lib/uaa/token_coder.rb
160
173
  - lib/uaa/token_issuer.rb
@@ -1,30 +0,0 @@
1
- #--
2
- # Cloud Foundry
3
- # Copyright (c) [2009-2014] Pivotal Software, Inc. All Rights Reserved.
4
- #
5
- # This product is licensed to you under the Apache License, Version 2.0 (the "License").
6
- # You may not use this product except in compliance with the License.
7
- #
8
- # This product includes a number of subcomponents with
9
- # separate copyright notices and license terms. Your use of these
10
- # subcomponents is subject to the terms and conditions of the
11
- # subcomponent's license, as noted in the LICENSE file.
12
- #++
13
-
14
- module CF::UAA
15
- module ProxyOptions
16
- def proxy_options_for(uri)
17
- ssl = uri.is_a?(URI::HTTPS)
18
- proxy_to_use = (ssl ? https_proxy : http_proxy)
19
-
20
- if proxy_to_use
21
- proxy_to_use = "proto://#{proxy_to_use}" unless proxy_to_use =~ /:\/\//
22
- proxy_uri = URI.parse(proxy_to_use)
23
- proxy_user, proxy_password = proxy_uri.userinfo.split(/:/) if proxy_uri.userinfo
24
- [proxy_uri.host, proxy_uri.port, proxy_user, proxy_password]
25
- else
26
- []
27
- end
28
- end
29
- end
30
- end