authlete 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YzFlNzMwMjg5NmRlNDkzNzMyZmEwNGNiNTVmNTYzNTZkZGYxZmE1Nw==
5
- data.tar.gz: !binary |-
6
- Nzk3N2FkNTZlZWI1NDQ5NDkxMTA1Y2FlZmE4NTE1YjY1ZDc0NmQ5NQ==
2
+ SHA1:
3
+ metadata.gz: 682e3b913cbb71a411f3a9329c35d37e2145d022
4
+ data.tar.gz: 1d76ad9990fbd0d0b0970af183aad66043167347
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- ZmVmNjE0YzI3YjdjNWZjZGY2N2QyMWYwMDk4NGY0ODY5NTUxMjNiYjQ3ZGU1
10
- NTdmYTVkOGE2YmMxZTNmZmM2Zjc0MmIzNzQ4OGM1NTQzMGYwMDc4ZjY2Zjlm
11
- NDMwNzAwZGE3MTA0YWQ2ZDFmN2MzNjgyZDllZDkxODhjNzkyM2Y=
12
- data.tar.gz: !binary |-
13
- ZGY3NmE1YmMwZmY4YzhmMmI3YWI4YmIyNjUxOTc4OGZiZTVjYWJkMTIxODNi
14
- OWM2NmFkZGUwMjc4ODJlNWQyZjc1ZDI5ODdkZWRlNzc2ZjUwZDQ1NGNlYTQy
15
- Y2ExYTk0ZjY1MTM0ZTVlMzJlMTFlODU5YjJiYWFiYTcwMTU1ZjQ=
6
+ metadata.gz: 603c084a438a9b2926220b31f972113b42da06084f81ac4da4ab1bf3ad19f6a52f3a86dbfc5d480c0e07c30638f52aa57290e25d28c92f6c9a6c062cac0e02ed
7
+ data.tar.gz: aa4dc9290c20512d06a4c641b04c7f2f252d9746d3bce4889d4a1d27c1208841c53671da5a0fb702451a1827cdcfae0835ab36ff6bc2af9e0d72a0db1178320d
data/lib/authlete/api.rb CHANGED
@@ -1,460 +1,460 @@
1
- # :nodoc:
2
- #
3
- # Copyright (C) 2014-2015 Authlete, Inc.
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
-
17
-
18
- require 'json'
19
- require 'rack'
20
- require 'rest-client'
21
-
22
-
23
- module Authlete
24
- # == Authlete::Api Module
25
- #
26
- # A web client that accesses Authlete Web APIs.
27
- #
28
- class Api
29
- include Authlete::Utility
30
-
31
- # The host which provides Authlete Web APIs.
32
- # For example, <tt>https://evaluation-dot-authlete.appspot.com</tt>
33
- attr_accessor :host
34
-
35
- # The API key of a service owner.
36
- attr_accessor :service_owner_api_key
37
-
38
- # The API secret of a service owner.
39
- attr_accessor :service_owner_api_secret
40
-
41
- # The API key of a service.
42
- attr_accessor :service_api_key
43
-
44
- # The API secret of a service.
45
- attr_accessor :service_api_secret
46
-
47
- # Extra HTTP headers
48
- attr_accessor :extra_headers
49
-
50
- private
51
-
52
- # The constructor which takes a hash containing configuration
53
- # parameters. Valid configuration parameter names are as follows.
54
- #
55
- # - <tt>:host</tt>
56
- # - <tt>:service_owner_api_key</tt>
57
- # - <tt>:service_owner_api_secret</tt>
58
- # - <tt>:service_api_key</tt>
59
- # - <tt>:service_api_secret</tt>
60
- #
61
- def initialize(config = {})
62
- @host = extract_value(config, :host)
63
- @service_owner_api_key = extract_value(config, :service_owner_api_key)
64
- @service_owner_api_secret = extract_value(config, :service_owner_api_secret)
65
- @service_api_key = extract_value(config, :service_api_key)
66
- @service_api_secret = extract_value(config, :service_api_secret)
67
- end
68
-
69
- def call_api(method, path, content_type, payload, user, password)
70
- headers = {}
71
-
72
- if content_type.nil? == false
73
- headers.merge!(:content_type => content_type)
74
- end
75
-
76
- if @extra_headers.nil? == false
77
- headers.merge!(@extra_headers)
78
- end
79
-
80
- response = execute(
81
- :method => method,
82
- :url => @host + path,
83
- :headers => headers,
84
- :payload => payload,
85
- :user => user,
86
- :password => password
87
- )
88
-
89
- body = body_as_string(response)
90
-
91
- if body.nil?
92
- return nil
93
- end
94
-
95
- JSON.parse(response.body.to_s, :symbolize_names => true)
96
- end
97
-
98
- def execute(parameters)
99
- begin
100
- return RestClient::Request.new(parameters).execute
101
- rescue => e
102
- raise_api_exception(e)
103
- end
104
- end
105
-
106
- def raise_api_exception(exception)
107
- message = exception.message
108
- response = exception.response
109
-
110
- if response.nil?
111
- # Raise an error without HTTP response information.
112
- raise Authlete::Exception.new(:message => message)
113
- end
114
-
115
- # Raise an error with HTTP response information.
116
- raise_api_exception_with_http_response_info(message, response.code, response.body)
117
- end
118
-
119
- def raise_api_exception_with_http_response_info(message, status_code, response_body)
120
- # Parse the response body as a json.
121
- json = parse_response_body(response_body, message, status_code)
122
-
123
- # If the json has the HTTP response information from an Authlete API.
124
- if has_authlete_api_response_info(json)
125
- # Raise an error with it.
126
- hash = json.merge!(:statusCode => status_code)
127
- raise Authlete::Exception.new(hash)
128
- end
129
-
130
- # Raise an error with 'status_code' and the original error message.
131
- raise Authlete::Exception.new(
132
- :message => message,
133
- :status_code => status_code
134
- )
135
- end
136
-
137
- def parse_response_body(response_body, message, status_code)
138
- begin
139
- return JSON.parse(response_body.to_s, :symbolize_names => true)
140
- rescue
141
- # Failed to parse the response body as a json.
142
- raise Authlete::Exception.new(
143
- :message => message,
144
- :status_code => status_code
145
- )
146
- end
147
- end
148
-
149
- def has_authlete_api_response_info(json)
150
- json && json.key?(:resultCode) && json.key?(:resultMessage)
151
- end
152
-
153
- def body_as_string(response)
154
- if response.body.nil?
155
- return nil
156
- end
157
-
158
- body = response.body.to_s
159
-
160
- if body.length == 0
161
- return nil
162
- end
163
-
164
- return body
165
- end
166
-
167
- def call_api_service_owner(method, path, content_type, payload)
168
- call_api(method, path, content_type, payload, @service_owner_api_key, @service_owner_api_secret)
169
- end
170
-
171
- def call_api_service(method, path, content_type, payload)
172
- call_api(method, path, content_type, payload, @service_api_key, @service_api_secret)
173
- end
174
-
175
- def call_api_json(path, body, user, password)
176
- call_api(:post, path, 'application/json;charset=UTF-8', JSON.generate(body), user, password)
177
- end
178
-
179
- def call_api_json_service_owner(path, body)
180
- call_api_json(path, body, @service_owner_api_key, @service_owner_api_secret)
181
- end
182
-
183
- def call_api_json_service(path, body)
184
- call_api_json(path, body, @service_api_key, @service_api_secret)
185
- end
186
-
187
- def build_error_message(path, exception)
188
- begin
189
- # Use "resultMessage" if the response can be parsed as JSON.
190
- JSON.parse(exception.response.to_str)['resultMessage']
191
- rescue
192
- # Build a generic error message.
193
- "Authlete's #{path} API failed."
194
- end
195
- end
196
-
197
- def emit_rack_error_message(request, message)
198
- begin
199
- # Logging if possible.
200
- request.env['rack.errors'].write("ERROR: #{message}\n")
201
- rescue => e
202
- end
203
- end
204
-
205
- def to_query(params)
206
- if params.nil? || params.size == 0
207
- return ""
208
- end
209
-
210
- array = []
211
-
212
- params.each do |key, value|
213
- array.push("#{key}=#{value}")
214
- end
215
-
216
- return "?" + array.join("&")
217
- end
218
-
219
- public
220
-
221
- # Call Authlete's /api/service/create API.
222
- #
223
- # <tt>service</tt> is the content of a new service to create. The type of
224
- # the given object is either <tt>Hash</tt> or any object which
225
- # responds to <tt>to_hash</tt>. In normal cases, Authlete::Model::Service
226
- # (which responds to <tt>to_hash</tt>) should be used.
227
- #
228
- # On success, an instance of Authlete::Model::ServiceList is returned.
229
- # On error, RestClient::Exception (of rest-client GEM) is raised.
230
- def service_create(service)
231
- if service.kind_of?(Hash) == false
232
- if service.respond_to?('to_hash')
233
- service = service.to_hash
234
- end
235
- end
236
-
237
- hash = call_api_json_service_owner("/api/service/create", service)
238
-
239
- Authlete::Model::Service.new(hash)
240
- end
241
-
242
- # Call Authlete's /api/service/delete/{api_key} API.
243
- #
244
- # On error, RestClient::Exception (of rest-client GEM) is raised.
245
- def service_delete(api_key)
246
- call_api_service_owner(:delete, "/api/service/delete/#{api_key}", nil, nil)
247
- end
248
-
249
-
250
- # Call Authlete's /api/service/get/{api_key} API.
251
- #
252
- # <tt>api_key</tt> is the API key of the service whose information
253
- # you want to get.
254
- #
255
- # On success, an instance of Authlete::Model::Service is returned.
256
- # On error, RestClient::Exception (of rest-client GEM) is raised.
257
- def service_get(api_key)
258
- hash = call_api_service_owner(:get, "/api/service/get/#{api_key}", nil, nil)
259
-
260
- Authlete::Model::Service.new(hash)
261
- end
262
-
263
- # Call Authlete's /api/service/get/list API.
264
- #
265
- # <tt>params</tt> is an optional hash which contains query parameters
266
- # for /api/service/get/list API. <tt>:start</tt> and <tt>:end</tt> are
267
- # a start index (inclusive) and an end index (exclusive), respectively.
268
- #
269
- # On success, an instance of Authlete::Model::ServiceList is returned.
270
- # On error, RestClient::Exception (of rest-client GEM) is raised.
271
- def service_get_list(params = nil)
272
- hash = call_api_service_owner(:get, "/api/service/get/list#{to_query(params)}", nil, nil)
273
-
274
- Authlete::Model::ServiceList.new(hash)
275
- end
276
-
277
-
278
- # Call Authlete's /api/service/update/{api_key} API.
279
- #
280
- # <tt>api_key</tt> is the API key of the service whose information
281
- # you want to get.
282
- #
283
- # <tt>service</tt> is the new content of the service. The type of
284
- # the given object is either <tt>Hash</tt> or any object which
285
- # responds to <tt>to_hash</tt>. In normal cases, Authlete::Model::Service
286
- # (which responds to <tt>to_hash</tt>) should be used.
287
- #
288
- # On success, an instance of Authlete::Model::Service is returned.
289
- # On error, RestClient::Exception (of rest-client GEM) is raised.
290
- def service_update(api_key, service)
291
- if service.kind_of?(Hash) == false
292
- if service.respond_to?('to_hash')
293
- service = service.to_hash
294
- end
295
- end
296
-
297
- hash = call_api_json_service_owner("/api/service/update/#{api_key}", service)
298
-
299
- Authlete::Model::Service.new(hash)
300
- end
301
-
302
- # Call Authlete's /api/client/create API.
303
- #
304
- # <tt>client</tt> is the content of a new service to create. The type of
305
- # the given object is either <tt>Hash</tt> or any object which
306
- # responds to <tt>to_hash</tt>. In normal cases, Authlete::Model::Client
307
- # (which responds to <tt>to_hash</tt>) should be used.
308
- #
309
- # On success, an instance of Authlete::Model::ClientList is returned.
310
- # On error, RestClient::Exception (of rest-client GEM) is raised.
311
- def client_create(client)
312
- if client.kind_of?(Hash) == false
313
- if client.respond_to?('to_hash')
314
- client = client.to_hash
315
- end
316
- end
317
-
318
- hash = call_api_json_service("/api/client/create", client)
319
-
320
- Authlete::Model::Client.new(hash)
321
- end
322
-
323
- # Call Authlete's /api/client/delete/{clientId} API.
324
- #
325
- # On error, RestClient::Exception (of rest-client GEM) is raised.
326
- def client_delete(clientId)
327
- call_api_service(:delete, "/api/client/delete/#{clientId}", nil, nil)
328
- end
329
-
330
- # Call Authlete's /api/client/get/{clientId} API.
331
- #
332
- # On success, an instance of Authlete::Model::Service is returned.
333
- # On error, RestClient::Exception (of rest-client GEM) is raised.
334
- def client_get(clientId)
335
- hash = call_api_service(:get, "/api/client/get/#{clientId}", nil, nil)
336
-
337
- Authlete::Model::Client.new(hash)
338
- end
339
-
340
- # Call Authlete's /api/client/get/list API.
341
- #
342
- # <tt>params</tt> is an optional hash which contains query parameters
343
- # for /api/client/get/list API. <tt>:start</tt> and <tt>:end</tt> are
344
- # a start index (inclusive) and an end index (exclusive), respectively.
345
- #
346
- # On success, an instance of Authlete::Model::ClientList is returned.
347
- # On error, RestClient::Exception (of rest-client GEM) is raised.
348
- def client_get_list(params = nil)
349
- hash = call_api_service(:get, "/api/client/get/list#{to_query(params)}", nil, nil)
350
-
351
- Authlete::Model::ClientList.new(hash)
352
- end
353
-
354
- # Call Authlete's /api/client/update/{clientId} API.
355
- #
356
- # <tt>client</tt> is the new content of the client. The type of
357
- # the given object is either <tt>Hash</tt> or any object which
358
- # responds to <tt>to_hash</tt>. In normal cases, Authlete::Model::Client
359
- # (which responds to <tt>to_hash</tt>) should be used.
360
- #
361
- # On success, an instance of Authlete::Model::Client is returned.
362
- # On error, RestClient::Exception (of rest-client GEM) is raised.
363
- def client_update(client)
364
- if client.kind_of?(Hash) == false
365
- if client.respond_to?('to_hash')
366
- client = client.to_hash
367
- end
368
- end
369
-
370
- hash = call_api_json_service("/api/client/update/#{client[:clientId]}", client)
371
-
372
- Authlete::Model::Client.new(hash)
373
- end
374
-
375
- # Call Authlete's {/auth/introspection}
376
- # [https://www.authlete.com/authlete_web_apis_introspection.html#auth_introspection]
377
- # API.
378
- #
379
- # <tt>token</tt> is an access token presented by a client application.
380
- # This is a must parameter. In a typical case, a client application uses
381
- # one of the means listed in {RFC 6750}[https://tools.ietf.org/html/rfc6750]
382
- # to present an access token to a {protected resource endpoint}
383
- # [https://tools.ietf.org/html/rfc6749#section-7].
384
- #
385
- # <tt>scopes</tt> is an array of scope names. This is an optional parameter.
386
- # When the specified scopes are not covered by the access token, Authlete
387
- # prepares the content of the error response.
388
- #
389
- # <tt>subject</tt> is a unique identifier of an end-user. This is an optional
390
- # parameter. When the access token is not associated with the specified
391
- # subject, Authlete prepares the content of the error response.
392
- #
393
- # On success, this method returns an instance of
394
- # <tt>Authlete::Response::IntrospectionResponse</tt>. On error, this method
395
- # throws <tt>RestClient::Exception</tt>.
396
- def introspection(token, scopes = nil, subject = nil)
397
- hash = call_api_json_service('/api/auth/introspection',
398
- :token => token, :scopes => scopes, :subject => subject)
399
-
400
- Authlete::Response::IntrospectionResponse.new(hash)
401
- end
402
-
403
-
404
- # Ensure that the request contains a valid access token.
405
- #
406
- # This method extracts an access token from the given request based on the
407
- # rules described in RFC 6750 and introspects the access token by calling
408
- # Authlete's /auth/introspection API.
409
- #
410
- # The first argument <tt>request</tt> is a Rack request.
411
- #
412
- # The second argument <tt>scopes</tt> is an array of scope names required
413
- # to access the target protected resource. This argument is optional.
414
- #
415
- # The third argument <tt>subject</tt> is a string which representing a
416
- # subject which has to be associated with the access token. This argument
417
- # is optional.
418
- #
419
- # This method returns an instance of
420
- # <tt>Authlete::Response::IntrospectionResponse</tt>. If its <tt>action</tt>
421
- # method returns 'OK', it means that the access token exists, has not
422
- # expired, covers the requested scopes (if specified), and is associated
423
- # with the requested subject (if specified). Otherwise, it means that the
424
- # request does not contain any access token or that the access token does
425
- # not satisfy the conditions to access the target protected resource.
426
- def protect_resource(request, scopes = nil, subject = nil)
427
- # Extract an access token from the request.
428
- access_token = extract_access_token(request)
429
-
430
- # If the request does not contain any access token.
431
- if access_token.nil?
432
- # The request does not contain a valid access token.
433
- return Authlete::Response::IntrospectionResponse.new(
434
- :action => 'BAD_REQUEST',
435
- :responseContent => 'Bearer error="invalid_token",error_description="The request does not contain a valid access token."'
436
- )
437
- end
438
-
439
- begin
440
- # Call Authlete's /auth/introspection API to introspect the access token.
441
- result = introspection(access_token, scopes, subject)
442
- rescue => e
443
- # Error message.
444
- message = build_error_message('/auth/introspection', e)
445
-
446
- # Emit a Rack error message.
447
- emit_rack_error_message(request, message)
448
-
449
- # Failed to introspect the access token.
450
- return Authlete::Response::IntrospectionResponse.new(
451
- :action => 'INTERNAL_SERVER_ERROR',
452
- :responseContent => "Bearer error=\"server_error\",error_description=\"#{message}\""
453
- )
454
- end
455
-
456
- # Return the response from Authlete's /auth/introspection API.
457
- result
458
- end
459
- end
460
- end
1
+ # :nodoc:
2
+ #
3
+ # Copyright (C) 2014-2015 Authlete, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ require 'json'
19
+ require 'rack'
20
+ require 'rest-client'
21
+
22
+
23
+ module Authlete
24
+ # == Authlete::Api Module
25
+ #
26
+ # A web client that accesses Authlete Web APIs.
27
+ #
28
+ class Api
29
+ include Authlete::Utility
30
+
31
+ # The host which provides Authlete Web APIs.
32
+ # For example, <tt>https://evaluation-dot-authlete.appspot.com</tt>
33
+ attr_accessor :host
34
+
35
+ # The API key of a service owner.
36
+ attr_accessor :service_owner_api_key
37
+
38
+ # The API secret of a service owner.
39
+ attr_accessor :service_owner_api_secret
40
+
41
+ # The API key of a service.
42
+ attr_accessor :service_api_key
43
+
44
+ # The API secret of a service.
45
+ attr_accessor :service_api_secret
46
+
47
+ # Extra HTTP headers
48
+ attr_accessor :extra_headers
49
+
50
+ private
51
+
52
+ # The constructor which takes a hash containing configuration
53
+ # parameters. Valid configuration parameter names are as follows.
54
+ #
55
+ # - <tt>:host</tt>
56
+ # - <tt>:service_owner_api_key</tt>
57
+ # - <tt>:service_owner_api_secret</tt>
58
+ # - <tt>:service_api_key</tt>
59
+ # - <tt>:service_api_secret</tt>
60
+ #
61
+ def initialize(config = {})
62
+ @host = extract_value(config, :host)
63
+ @service_owner_api_key = extract_value(config, :service_owner_api_key)
64
+ @service_owner_api_secret = extract_value(config, :service_owner_api_secret)
65
+ @service_api_key = extract_value(config, :service_api_key)
66
+ @service_api_secret = extract_value(config, :service_api_secret)
67
+ end
68
+
69
+ def call_api(method, path, content_type, payload, user, password)
70
+ headers = {}
71
+
72
+ if content_type.nil? == false
73
+ headers.merge!(:content_type => content_type)
74
+ end
75
+
76
+ if @extra_headers.nil? == false
77
+ headers.merge!(@extra_headers)
78
+ end
79
+
80
+ response = execute(
81
+ :method => method,
82
+ :url => @host + path,
83
+ :headers => headers,
84
+ :payload => payload,
85
+ :user => user,
86
+ :password => password
87
+ )
88
+
89
+ body = body_as_string(response)
90
+
91
+ if body.nil?
92
+ return nil
93
+ end
94
+
95
+ JSON.parse(response.body.to_s, :symbolize_names => true)
96
+ end
97
+
98
+ def execute(parameters)
99
+ begin
100
+ return RestClient::Request.new(parameters).execute
101
+ rescue => e
102
+ raise_api_exception(e)
103
+ end
104
+ end
105
+
106
+ def raise_api_exception(exception)
107
+ message = exception.message
108
+ response = exception.response
109
+
110
+ if response.nil?
111
+ # Raise an error without HTTP response information.
112
+ raise Authlete::Exception.new(:message => message)
113
+ end
114
+
115
+ # Raise an error with HTTP response information.
116
+ raise_api_exception_with_http_response_info(message, response.code, response.body)
117
+ end
118
+
119
+ def raise_api_exception_with_http_response_info(message, status_code, response_body)
120
+ # Parse the response body as a json.
121
+ json = parse_response_body(response_body, message, status_code)
122
+
123
+ # If the json has the HTTP response information from an Authlete API.
124
+ if has_authlete_api_response_info(json)
125
+ # Raise an error with it.
126
+ hash = json.merge!(:statusCode => status_code)
127
+ raise Authlete::Exception.new(hash)
128
+ end
129
+
130
+ # Raise an error with 'status_code' and the original error message.
131
+ raise Authlete::Exception.new(
132
+ :message => message,
133
+ :status_code => status_code
134
+ )
135
+ end
136
+
137
+ def parse_response_body(response_body, message, status_code)
138
+ begin
139
+ return JSON.parse(response_body.to_s, :symbolize_names => true)
140
+ rescue
141
+ # Failed to parse the response body as a json.
142
+ raise Authlete::Exception.new(
143
+ :message => message,
144
+ :status_code => status_code
145
+ )
146
+ end
147
+ end
148
+
149
+ def has_authlete_api_response_info(json)
150
+ json && json.key?(:resultCode) && json.key?(:resultMessage)
151
+ end
152
+
153
+ def body_as_string(response)
154
+ if response.body.nil?
155
+ return nil
156
+ end
157
+
158
+ body = response.body.to_s
159
+
160
+ if body.length == 0
161
+ return nil
162
+ end
163
+
164
+ return body
165
+ end
166
+
167
+ def call_api_service_owner(method, path, content_type, payload)
168
+ call_api(method, path, content_type, payload, @service_owner_api_key, @service_owner_api_secret)
169
+ end
170
+
171
+ def call_api_service(method, path, content_type, payload)
172
+ call_api(method, path, content_type, payload, @service_api_key, @service_api_secret)
173
+ end
174
+
175
+ def call_api_json(path, body, user, password)
176
+ call_api(:post, path, 'application/json;charset=UTF-8', JSON.generate(body), user, password)
177
+ end
178
+
179
+ def call_api_json_service_owner(path, body)
180
+ call_api_json(path, body, @service_owner_api_key, @service_owner_api_secret)
181
+ end
182
+
183
+ def call_api_json_service(path, body)
184
+ call_api_json(path, body, @service_api_key, @service_api_secret)
185
+ end
186
+
187
+ def build_error_message(path, exception)
188
+ begin
189
+ # Use "resultMessage" if the response can be parsed as JSON.
190
+ JSON.parse(exception.response.to_str)['resultMessage']
191
+ rescue
192
+ # Build a generic error message.
193
+ "Authlete's #{path} API failed."
194
+ end
195
+ end
196
+
197
+ def emit_rack_error_message(request, message)
198
+ begin
199
+ # Logging if possible.
200
+ request.env['rack.errors'].write("ERROR: #{message}\n")
201
+ rescue => e
202
+ end
203
+ end
204
+
205
+ def to_query(params)
206
+ if params.nil? || params.size == 0
207
+ return ""
208
+ end
209
+
210
+ array = []
211
+
212
+ params.each do |key, value|
213
+ array.push("#{key}=#{value}")
214
+ end
215
+
216
+ return "?" + array.join("&")
217
+ end
218
+
219
+ public
220
+
221
+ # Call Authlete's /api/service/create API.
222
+ #
223
+ # <tt>service</tt> is the content of a new service to create. The type of
224
+ # the given object is either <tt>Hash</tt> or any object which
225
+ # responds to <tt>to_hash</tt>. In normal cases, Authlete::Model::Service
226
+ # (which responds to <tt>to_hash</tt>) should be used.
227
+ #
228
+ # On success, an instance of Authlete::Model::ServiceList is returned.
229
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
230
+ def service_create(service)
231
+ if service.kind_of?(Hash) == false
232
+ if service.respond_to?('to_hash')
233
+ service = service.to_hash
234
+ end
235
+ end
236
+
237
+ hash = call_api_json_service_owner("/api/service/create", service)
238
+
239
+ Authlete::Model::Service.new(hash)
240
+ end
241
+
242
+ # Call Authlete's /api/service/delete/{api_key} API.
243
+ #
244
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
245
+ def service_delete(api_key)
246
+ call_api_service_owner(:delete, "/api/service/delete/#{api_key}", nil, nil)
247
+ end
248
+
249
+
250
+ # Call Authlete's /api/service/get/{api_key} API.
251
+ #
252
+ # <tt>api_key</tt> is the API key of the service whose information
253
+ # you want to get.
254
+ #
255
+ # On success, an instance of Authlete::Model::Service is returned.
256
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
257
+ def service_get(api_key)
258
+ hash = call_api_service_owner(:get, "/api/service/get/#{api_key}", nil, nil)
259
+
260
+ Authlete::Model::Service.new(hash)
261
+ end
262
+
263
+ # Call Authlete's /api/service/get/list API.
264
+ #
265
+ # <tt>params</tt> is an optional hash which contains query parameters
266
+ # for /api/service/get/list API. <tt>:start</tt> and <tt>:end</tt> are
267
+ # a start index (inclusive) and an end index (exclusive), respectively.
268
+ #
269
+ # On success, an instance of Authlete::Model::ServiceList is returned.
270
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
271
+ def service_get_list(params = nil)
272
+ hash = call_api_service_owner(:get, "/api/service/get/list#{to_query(params)}", nil, nil)
273
+
274
+ Authlete::Model::ServiceList.new(hash)
275
+ end
276
+
277
+
278
+ # Call Authlete's /api/service/update/{api_key} API.
279
+ #
280
+ # <tt>api_key</tt> is the API key of the service whose information
281
+ # you want to get.
282
+ #
283
+ # <tt>service</tt> is the new content of the service. The type of
284
+ # the given object is either <tt>Hash</tt> or any object which
285
+ # responds to <tt>to_hash</tt>. In normal cases, Authlete::Model::Service
286
+ # (which responds to <tt>to_hash</tt>) should be used.
287
+ #
288
+ # On success, an instance of Authlete::Model::Service is returned.
289
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
290
+ def service_update(api_key, service)
291
+ if service.kind_of?(Hash) == false
292
+ if service.respond_to?('to_hash')
293
+ service = service.to_hash
294
+ end
295
+ end
296
+
297
+ hash = call_api_json_service_owner("/api/service/update/#{api_key}", service)
298
+
299
+ Authlete::Model::Service.new(hash)
300
+ end
301
+
302
+ # Call Authlete's /api/client/create API.
303
+ #
304
+ # <tt>client</tt> is the content of a new service to create. The type of
305
+ # the given object is either <tt>Hash</tt> or any object which
306
+ # responds to <tt>to_hash</tt>. In normal cases, Authlete::Model::Client
307
+ # (which responds to <tt>to_hash</tt>) should be used.
308
+ #
309
+ # On success, an instance of Authlete::Model::ClientList is returned.
310
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
311
+ def client_create(client)
312
+ if client.kind_of?(Hash) == false
313
+ if client.respond_to?('to_hash')
314
+ client = client.to_hash
315
+ end
316
+ end
317
+
318
+ hash = call_api_json_service("/api/client/create", client)
319
+
320
+ Authlete::Model::Client.new(hash)
321
+ end
322
+
323
+ # Call Authlete's /api/client/delete/{clientId} API.
324
+ #
325
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
326
+ def client_delete(clientId)
327
+ call_api_service(:delete, "/api/client/delete/#{clientId}", nil, nil)
328
+ end
329
+
330
+ # Call Authlete's /api/client/get/{clientId} API.
331
+ #
332
+ # On success, an instance of Authlete::Model::Service is returned.
333
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
334
+ def client_get(clientId)
335
+ hash = call_api_service(:get, "/api/client/get/#{clientId}", nil, nil)
336
+
337
+ Authlete::Model::Client.new(hash)
338
+ end
339
+
340
+ # Call Authlete's /api/client/get/list API.
341
+ #
342
+ # <tt>params</tt> is an optional hash which contains query parameters
343
+ # for /api/client/get/list API. <tt>:start</tt> and <tt>:end</tt> are
344
+ # a start index (inclusive) and an end index (exclusive), respectively.
345
+ #
346
+ # On success, an instance of Authlete::Model::ClientList is returned.
347
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
348
+ def client_get_list(params = nil)
349
+ hash = call_api_service(:get, "/api/client/get/list#{to_query(params)}", nil, nil)
350
+
351
+ Authlete::Model::ClientList.new(hash)
352
+ end
353
+
354
+ # Call Authlete's /api/client/update/{clientId} API.
355
+ #
356
+ # <tt>client</tt> is the new content of the client. The type of
357
+ # the given object is either <tt>Hash</tt> or any object which
358
+ # responds to <tt>to_hash</tt>. In normal cases, Authlete::Model::Client
359
+ # (which responds to <tt>to_hash</tt>) should be used.
360
+ #
361
+ # On success, an instance of Authlete::Model::Client is returned.
362
+ # On error, RestClient::Exception (of rest-client GEM) is raised.
363
+ def client_update(client)
364
+ if client.kind_of?(Hash) == false
365
+ if client.respond_to?('to_hash')
366
+ client = client.to_hash
367
+ end
368
+ end
369
+
370
+ hash = call_api_json_service("/api/client/update/#{client[:clientId]}", client)
371
+
372
+ Authlete::Model::Client.new(hash)
373
+ end
374
+
375
+ # Call Authlete's {/auth/introspection}
376
+ # [https://www.authlete.com/authlete_web_apis_introspection.html#auth_introspection]
377
+ # API.
378
+ #
379
+ # <tt>token</tt> is an access token presented by a client application.
380
+ # This is a must parameter. In a typical case, a client application uses
381
+ # one of the means listed in {RFC 6750}[https://tools.ietf.org/html/rfc6750]
382
+ # to present an access token to a {protected resource endpoint}
383
+ # [https://tools.ietf.org/html/rfc6749#section-7].
384
+ #
385
+ # <tt>scopes</tt> is an array of scope names. This is an optional parameter.
386
+ # When the specified scopes are not covered by the access token, Authlete
387
+ # prepares the content of the error response.
388
+ #
389
+ # <tt>subject</tt> is a unique identifier of an end-user. This is an optional
390
+ # parameter. When the access token is not associated with the specified
391
+ # subject, Authlete prepares the content of the error response.
392
+ #
393
+ # On success, this method returns an instance of
394
+ # <tt>Authlete::Response::IntrospectionResponse</tt>. On error, this method
395
+ # throws <tt>RestClient::Exception</tt>.
396
+ def introspection(token, scopes = nil, subject = nil)
397
+ hash = call_api_json_service('/api/auth/introspection',
398
+ :token => token, :scopes => scopes, :subject => subject)
399
+
400
+ Authlete::Response::IntrospectionResponse.new(hash)
401
+ end
402
+
403
+
404
+ # Ensure that the request contains a valid access token.
405
+ #
406
+ # This method extracts an access token from the given request based on the
407
+ # rules described in RFC 6750 and introspects the access token by calling
408
+ # Authlete's /auth/introspection API.
409
+ #
410
+ # The first argument <tt>request</tt> is a Rack request.
411
+ #
412
+ # The second argument <tt>scopes</tt> is an array of scope names required
413
+ # to access the target protected resource. This argument is optional.
414
+ #
415
+ # The third argument <tt>subject</tt> is a string which representing a
416
+ # subject which has to be associated with the access token. This argument
417
+ # is optional.
418
+ #
419
+ # This method returns an instance of
420
+ # <tt>Authlete::Response::IntrospectionResponse</tt>. If its <tt>action</tt>
421
+ # method returns 'OK', it means that the access token exists, has not
422
+ # expired, covers the requested scopes (if specified), and is associated
423
+ # with the requested subject (if specified). Otherwise, it means that the
424
+ # request does not contain any access token or that the access token does
425
+ # not satisfy the conditions to access the target protected resource.
426
+ def protect_resource(request, scopes = nil, subject = nil)
427
+ # Extract an access token from the request.
428
+ access_token = extract_access_token(request)
429
+
430
+ # If the request does not contain any access token.
431
+ if access_token.nil?
432
+ # The request does not contain a valid access token.
433
+ return Authlete::Response::IntrospectionResponse.new(
434
+ :action => 'BAD_REQUEST',
435
+ :responseContent => 'Bearer error="invalid_token",error_description="The request does not contain a valid access token."'
436
+ )
437
+ end
438
+
439
+ begin
440
+ # Call Authlete's /auth/introspection API to introspect the access token.
441
+ result = introspection(access_token, scopes, subject)
442
+ rescue => e
443
+ # Error message.
444
+ message = build_error_message('/auth/introspection', e)
445
+
446
+ # Emit a Rack error message.
447
+ emit_rack_error_message(request, message)
448
+
449
+ # Failed to introspect the access token.
450
+ return Authlete::Response::IntrospectionResponse.new(
451
+ :action => 'INTERNAL_SERVER_ERROR',
452
+ :responseContent => "Bearer error=\"server_error\",error_description=\"#{message}\""
453
+ )
454
+ end
455
+
456
+ # Return the response from Authlete's /auth/introspection API.
457
+ result
458
+ end
459
+ end
460
+ end