haveapi 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/doc/protocol.md +27 -10
- data/haveapi.gemspec +1 -2
- data/lib/haveapi/action.rb +23 -7
- data/lib/haveapi/actions/default.rb +3 -3
- data/lib/haveapi/authentication/base.rb +19 -1
- data/lib/haveapi/authentication/basic/provider.rb +7 -1
- data/lib/haveapi/authentication/chain.rb +10 -12
- data/lib/haveapi/authentication/token/action_config.rb +53 -0
- data/lib/haveapi/authentication/token/action_request.rb +23 -0
- data/lib/haveapi/authentication/token/action_result.rb +42 -0
- data/lib/haveapi/authentication/token/config.rb +115 -0
- data/lib/haveapi/authentication/token/provider.rb +259 -81
- data/lib/haveapi/authentication/token.rb +9 -0
- data/lib/haveapi/client_examples/curl.rb +3 -3
- data/lib/haveapi/client_examples/fs_client.rb +3 -3
- data/lib/haveapi/client_examples/http.rb +7 -7
- data/lib/haveapi/client_examples/js_client.rb +1 -1
- data/lib/haveapi/client_examples/php_client.rb +1 -1
- data/lib/haveapi/client_examples/ruby_cli.rb +1 -1
- data/lib/haveapi/client_examples/ruby_client.rb +1 -1
- data/lib/haveapi/context.rb +13 -13
- data/lib/haveapi/example.rb +3 -3
- data/lib/haveapi/exceptions.rb +2 -0
- data/lib/haveapi/extensions/exception_mailer.rb +8 -1
- data/lib/haveapi/model_adapters/active_record.rb +6 -6
- data/lib/haveapi/parameters/resource.rb +10 -10
- data/lib/haveapi/params.rb +1 -1
- data/lib/haveapi/resource.rb +18 -10
- data/lib/haveapi/resources/action_state.rb +2 -2
- data/lib/haveapi/route.rb +4 -3
- data/lib/haveapi/server.rb +7 -7
- data/lib/haveapi/spec/mock_action.rb +3 -3
- data/lib/haveapi/spec/spec_methods.rb +8 -8
- data/lib/haveapi/version.rb +2 -2
- data/lib/haveapi/views/version_page.erb +2 -2
- data/shell.nix +1 -1
- metadata +9 -5
- data/lib/haveapi/authentication/token/resources.rb +0 -110
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e44e03fec908d742be4499f22c97f16453cd53bd4a23a14928af94d3456bea2
|
4
|
+
data.tar.gz: '0339228a0003baae1528050052b930acedbad51651893e4ce8329945bf98a5f0'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 157bfeeaf24ea9304e96051c4a05e9454cdfbb818ac7476efbb7e587547305bbd1cb54bb9acfebf0f7ac3a8b553766b7527fa80a8af575d306df051e46394be6
|
7
|
+
data.tar.gz: 1dbfe70f5b5e7f15c1cc779df344a09afcc0912df4a74ac27ff51f45f65f3eef574dd66ff0fee80c7f6b44f093491c0242494fbd52af7b46a1674ad3971f1933
|
data/doc/protocol.md
CHANGED
@@ -94,14 +94,29 @@ HTTP basic authentication needs no other configuration, only informs about its p
|
|
94
94
|
|
95
95
|
"basic": {}
|
96
96
|
|
97
|
+
HTTP basic authentication does not support multi-factor authentication.
|
98
|
+
|
97
99
|
### Token authentication
|
98
|
-
Token authentication contains
|
99
|
-
|
100
|
+
Token authentication contains resource ``token``, that is used to acquire
|
101
|
+
and revoke tokens.
|
102
|
+
|
103
|
+
Tokens are acquired by action ``request``, in which the client provides arbitrary
|
104
|
+
login credentials. If the login credentials match, the server either concludes
|
105
|
+
the authentication process, or multiple authentication steps may be necessary.
|
106
|
+
|
107
|
+
For single-step authentication processses, action ``request`` returns the
|
108
|
+
token and its validity period. If multiple authentication steps are necessary,
|
109
|
+
the server signals this by returning ``complete = true``. The client then has
|
110
|
+
to call the next authentication step, which is an action on the ``token``
|
111
|
+
resource, as returned in ``next_action``.
|
100
112
|
|
101
|
-
|
102
|
-
|
113
|
+
Tokens are returned in both cases. If the authentication is finished, the token
|
114
|
+
is then used for authenticating further requests. If the authentication continues,
|
115
|
+
the token is used to authenticate the next authentication request, which then
|
116
|
+
returns another token.
|
103
117
|
|
104
|
-
|
118
|
+
After the authentication is complete, acquired tokens can be renewed using action
|
119
|
+
``renew`` and revoked by calling the ``revoke`` action.
|
105
120
|
|
106
121
|
"token": {
|
107
122
|
"http_header": "<name of HTTP header to transfer token in, by default X-HaveAPI-Auth-Token>",
|
@@ -113,7 +128,7 @@ Token can be revoked by calling the ``revoke`` action.
|
|
113
128
|
"input": {
|
114
129
|
...
|
115
130
|
"parameters": {
|
116
|
-
"
|
131
|
+
"user": ...
|
117
132
|
"password": ...
|
118
133
|
"lifetime": ...
|
119
134
|
"interval": ...
|
@@ -125,6 +140,8 @@ Token can be revoked by calling the ``revoke`` action.
|
|
125
140
|
"parameters": {
|
126
141
|
"token": ...
|
127
142
|
"valid_to": ...
|
143
|
+
"complete": true/false
|
144
|
+
"next_action": ...
|
128
145
|
},
|
129
146
|
...
|
130
147
|
},
|
@@ -176,7 +193,7 @@ Every action is described as:
|
|
176
193
|
... list of examples ...
|
177
194
|
],
|
178
195
|
"meta": ... metadata ...,
|
179
|
-
"
|
196
|
+
"path": "URL for this action",
|
180
197
|
"method": "HTTP method to be used",
|
181
198
|
"help": "URL to get this very description of the action"
|
182
199
|
}
|
@@ -365,12 +382,12 @@ This is used for associations between resources, e.g. car has a wheel.
|
|
365
382
|
"value_id": "<name of a parameter that is used as an id>",
|
366
383
|
"value_label": "<name of a parameter that is used as a value>",
|
367
384
|
"value": {
|
368
|
-
"
|
385
|
+
"path": "URL to 'show' action of associated resource",
|
369
386
|
"method": "HTTP method to use",
|
370
387
|
"help": "URL to get the associated resource's 'show' description"
|
371
388
|
},
|
372
389
|
"choices": {
|
373
|
-
"
|
390
|
+
"path": "URL to action that returns a list of possible associations",
|
374
391
|
"method": "HTTP method to use",
|
375
392
|
"help": "URL to description of the list action"
|
376
393
|
}
|
@@ -391,7 +408,7 @@ render them according to its syntax.
|
|
391
408
|
|
392
409
|
{
|
393
410
|
"title": "A title",
|
394
|
-
"
|
411
|
+
"path_params: [ ... array of integers ... ],
|
395
412
|
"request": {
|
396
413
|
... a hash of request parameters ...
|
397
414
|
},
|
data/haveapi.gemspec
CHANGED
@@ -5,7 +5,6 @@ require 'haveapi/version'
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'haveapi'
|
7
7
|
s.version = HaveAPI::VERSION
|
8
|
-
s.date = '2017-11-27'
|
9
8
|
s.summary =
|
10
9
|
s.description = 'Framework for creating self-describing APIs'
|
11
10
|
s.authors = 'Jakub Skokan'
|
@@ -24,6 +23,6 @@ Gem::Specification.new do |s|
|
|
24
23
|
s.add_runtime_dependency 'rake'
|
25
24
|
s.add_runtime_dependency 'github-markdown'
|
26
25
|
s.add_runtime_dependency 'nesty', '~> 1.0'
|
27
|
-
s.add_runtime_dependency 'haveapi-client', '~> 0.
|
26
|
+
s.add_runtime_dependency 'haveapi-client', '~> 0.13.0'
|
28
27
|
s.add_runtime_dependency 'mail'
|
29
28
|
end
|
data/lib/haveapi/action.rb
CHANGED
@@ -31,7 +31,8 @@ module HaveAPI
|
|
31
31
|
attr_accessor :flags
|
32
32
|
|
33
33
|
class << self
|
34
|
-
|
34
|
+
attr_accessor :resource
|
35
|
+
attr_reader :authorization, :examples
|
35
36
|
|
36
37
|
def inherited(subclass)
|
37
38
|
# puts "Action.inherited called #{subclass} from #{to_s}"
|
@@ -45,7 +46,7 @@ module HaveAPI
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def delayed_inherited(subclass)
|
48
|
-
resource = Kernel.const_get(subclass.to_s.deconstantize)
|
49
|
+
resource = subclass.resource || Kernel.const_get(subclass.to_s.deconstantize)
|
49
50
|
|
50
51
|
inherit_attrs(subclass)
|
51
52
|
inherit_attrs_from_resource(subclass, resource, [:auth])
|
@@ -177,14 +178,29 @@ module HaveAPI
|
|
177
178
|
@examples << e
|
178
179
|
end
|
179
180
|
|
181
|
+
def action_name
|
182
|
+
(@action_name ? @action_name.to_s : to_s).demodulize
|
183
|
+
end
|
184
|
+
|
185
|
+
def action_name=(name)
|
186
|
+
@action_name = name
|
187
|
+
end
|
188
|
+
|
180
189
|
def build_route(prefix)
|
181
|
-
route = @route ||
|
190
|
+
route = @route || action_name.underscore
|
191
|
+
if @route
|
192
|
+
@route
|
193
|
+
elsif action_name
|
194
|
+
action_name.to_s.demodulize.underscore
|
195
|
+
else
|
196
|
+
to_s.demodulize.underscore
|
197
|
+
end
|
182
198
|
|
183
199
|
if !route.is_a?(String) && route.respond_to?(:call)
|
184
200
|
route = route.call(self.resource)
|
185
201
|
end
|
186
202
|
|
187
|
-
prefix + route % {resource: self.resource.
|
203
|
+
prefix + route % {resource: self.resource.resource_name.underscore}
|
188
204
|
end
|
189
205
|
|
190
206
|
def describe(context)
|
@@ -214,9 +230,9 @@ module HaveAPI
|
|
214
230
|
output: @output ? @output.describe(context) : {parameters: {}},
|
215
231
|
meta: @meta ? @meta.merge(@meta) { |_, v| v && v.describe(context) } : nil,
|
216
232
|
examples: @examples ? @examples.describe(context) : [],
|
217
|
-
|
233
|
+
path: context.resolved_path,
|
218
234
|
method: route_method,
|
219
|
-
help: "#{context.
|
235
|
+
help: "#{context.path}?method=#{route_method}"
|
220
236
|
}
|
221
237
|
end
|
222
238
|
|
@@ -245,7 +261,7 @@ module HaveAPI
|
|
245
261
|
ret
|
246
262
|
end
|
247
263
|
|
248
|
-
def
|
264
|
+
def resolve_path_params(object)
|
249
265
|
if self.resolve
|
250
266
|
self.resolve.call(object)
|
251
267
|
|
@@ -38,18 +38,18 @@ module HaveAPI
|
|
38
38
|
end
|
39
39
|
|
40
40
|
class Show < Action
|
41
|
-
route ->(r){ r.singular ? '' : '
|
41
|
+
route ->(r){ r.singular ? '' : '{%{resource}_id}' }
|
42
42
|
http_method :get
|
43
43
|
aliases %i(find)
|
44
44
|
end
|
45
45
|
|
46
46
|
class Update < Action
|
47
|
-
route ->(r){ r.singular ? '' : '
|
47
|
+
route ->(r){ r.singular ? '' : '{%{resource}_id}' }
|
48
48
|
http_method :put
|
49
49
|
end
|
50
50
|
|
51
51
|
class Delete < Action
|
52
|
-
route ->(r){ r.singular ? '' : '
|
52
|
+
route ->(r){ r.singular ? '' : '{%{resource}_id}' }
|
53
53
|
http_method :delete
|
54
54
|
aliases %i(destroy)
|
55
55
|
end
|
@@ -2,14 +2,32 @@ module HaveAPI
|
|
2
2
|
module Authentication
|
3
3
|
# Base class for authentication providers.
|
4
4
|
class Base
|
5
|
-
|
5
|
+
# Get or set auth method name
|
6
|
+
# @param v [Symbol, nil]
|
7
|
+
# @return [Symbol]
|
8
|
+
def self.auth_method(v = nil)
|
9
|
+
if v
|
10
|
+
@auth_method = v
|
11
|
+
else
|
12
|
+
@auth_method || name.split('::').last.underscore.to_sym
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Symbol]
|
17
|
+
attr_reader :name
|
6
18
|
|
7
19
|
def initialize(server, v)
|
20
|
+
@name = self.class.auth_method
|
8
21
|
@server = server
|
9
22
|
@version = v
|
10
23
|
setup
|
11
24
|
end
|
12
25
|
|
26
|
+
# @return [Module, nil]
|
27
|
+
def resource_module
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
13
31
|
# Reimplement this method in your authentication provider.
|
14
32
|
# +request+ is passed directly from Sinatra.
|
15
33
|
def authenticate(request)
|
@@ -17,12 +17,18 @@ module HaveAPI::Authentication
|
|
17
17
|
# ...
|
18
18
|
# api.auth_chain << MyBasicAuth
|
19
19
|
class Provider < Base
|
20
|
+
auth_method :basic
|
21
|
+
|
20
22
|
def authenticate(request)
|
21
23
|
user = nil
|
22
24
|
|
23
25
|
auth = Rack::Auth::Basic::Request.new(request.env)
|
24
26
|
if auth.provided? && auth.basic? && auth.credentials
|
25
|
-
|
27
|
+
begin
|
28
|
+
user = find_user(request, *auth.credentials)
|
29
|
+
rescue HaveAPI::AuthenticationError
|
30
|
+
user = nil
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
34
|
user
|
@@ -50,17 +50,17 @@ module HaveAPI::Authentication
|
|
50
50
|
|
51
51
|
def describe(context)
|
52
52
|
ret = {}
|
53
|
-
|
53
|
+
|
54
54
|
return ret unless @instances[context.version]
|
55
55
|
|
56
56
|
@instances[context.version].each do |provider|
|
57
57
|
ret[provider.name] = provider.describe
|
58
58
|
|
59
|
-
if provider.
|
59
|
+
if provider.resource_module
|
60
60
|
ret[provider.name][:resources] = {}
|
61
61
|
|
62
62
|
@server.routes[context.version][:authentication][provider.name][:resources].each do |r, children|
|
63
|
-
ret[provider.name][:resources][r.
|
63
|
+
ret[provider.name][:resources][r.resource_name.underscore.to_sym] = r.describe(children, context)
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -97,17 +97,15 @@ module HaveAPI::Authentication
|
|
97
97
|
protected
|
98
98
|
def register_provider(v, p)
|
99
99
|
instance = p.new(@server, v)
|
100
|
-
parts = p.superclass.name.split('::')
|
101
|
-
|
102
|
-
instance.name = parts[-2].underscore.to_sym
|
103
|
-
|
104
100
|
@instances[v] << instance
|
105
101
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
102
|
+
if resource_module = instance.resource_module
|
103
|
+
@server.add_auth_module(
|
104
|
+
v,
|
105
|
+
instance.name,
|
106
|
+
resource_module,
|
107
|
+
prefix: instance.name.to_s,
|
108
|
+
)
|
111
109
|
end
|
112
110
|
end
|
113
111
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module HaveAPI::Authentication
|
2
|
+
module Token
|
3
|
+
class ActionConfig
|
4
|
+
# @param block [Proc]
|
5
|
+
# @param opts [Hash]
|
6
|
+
# @option opts [Boolean] :input
|
7
|
+
# @option opts [Boolean] :handle
|
8
|
+
def initialize(block, opts = {})
|
9
|
+
@block = block
|
10
|
+
@opts = with_defaults(opts)
|
11
|
+
update(block)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param block [Proc]
|
15
|
+
def update(block)
|
16
|
+
instance_exec(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Configure input parameters in the context of {HaveAPI::Params}
|
20
|
+
def input(&block)
|
21
|
+
if block && check!(:input)
|
22
|
+
@input = block
|
23
|
+
else
|
24
|
+
@input
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Handle the action
|
29
|
+
# @yieldparam request [ActionRequest]
|
30
|
+
# @yieldparam result [ActionResult]
|
31
|
+
# @yieldreturn [ActionResult]
|
32
|
+
def handle(&block)
|
33
|
+
if block && check!(:handle)
|
34
|
+
@handle = block
|
35
|
+
else
|
36
|
+
@handle
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def check!(name)
|
42
|
+
fail "#{name} cannot be configured" unless @opts[name]
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def with_defaults(opts)
|
47
|
+
Hash[%i(input handle).map do |v|
|
48
|
+
[v, opts.has_key?(v) ? opts[v] : true]
|
49
|
+
end]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module HaveAPI::Authentication
|
2
|
+
module Token
|
3
|
+
class ActionRequest
|
4
|
+
# @return [Sinatra::Request]
|
5
|
+
attr_reader :request
|
6
|
+
|
7
|
+
# @return [Hash]
|
8
|
+
attr_reader :input
|
9
|
+
|
10
|
+
# @return [Object, nil]
|
11
|
+
attr_reader :user
|
12
|
+
|
13
|
+
# @return [String, nil]
|
14
|
+
attr_reader :token
|
15
|
+
|
16
|
+
def initialize(opts = {})
|
17
|
+
opts.each do |k, v|
|
18
|
+
instance_variable_set(:"@#{k}", v)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module HaveAPI::Authentication
|
2
|
+
module Token
|
3
|
+
class ActionResult
|
4
|
+
# @param complete [Boolean]
|
5
|
+
# @return [Boolean]
|
6
|
+
attr_accessor :complete
|
7
|
+
|
8
|
+
# @param error [String]
|
9
|
+
# @return [String, nil]
|
10
|
+
attr_accessor :error
|
11
|
+
|
12
|
+
# @param token [String]
|
13
|
+
# @return [String, nil]
|
14
|
+
attr_accessor :token
|
15
|
+
|
16
|
+
# @param valid_to [Time]
|
17
|
+
# @return [Time, nil]
|
18
|
+
attr_accessor :valid_to
|
19
|
+
|
20
|
+
# @param next_action [String]
|
21
|
+
# @return [String, nil]
|
22
|
+
attr_accessor :next_action
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@ok = false
|
26
|
+
end
|
27
|
+
|
28
|
+
def ok
|
29
|
+
@ok = true
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def ok?
|
34
|
+
@ok && @error.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def complete?
|
38
|
+
@complete ? true : false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module HaveAPI::Authentication
|
2
|
+
module Token
|
3
|
+
# Configuration for {HaveAPI::Authentication::Token::Provider}
|
4
|
+
#
|
5
|
+
# Create a subclass and use with {HaveAPI::Authentication::Token#with_config}.
|
6
|
+
class Config
|
7
|
+
class << self
|
8
|
+
# Configure token request action
|
9
|
+
def request(&block)
|
10
|
+
if block
|
11
|
+
if @request
|
12
|
+
@request.update(block)
|
13
|
+
else
|
14
|
+
@request = ActionConfig.new(block)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
@request
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
%i(renew revoke).each do |name|
|
22
|
+
# Configuration method
|
23
|
+
define_method(name) do |&block|
|
24
|
+
var = :"@#{name}"
|
25
|
+
val = instance_variable_get(var)
|
26
|
+
|
27
|
+
if block
|
28
|
+
if val
|
29
|
+
val.update(block)
|
30
|
+
else
|
31
|
+
instance_variable_set(var, ActionConfig.new(block, input: false))
|
32
|
+
end
|
33
|
+
else
|
34
|
+
val
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param name [Symbol]
|
40
|
+
def action(name, &block)
|
41
|
+
@actions ||= {}
|
42
|
+
@actions[name] = ActionConfig.new(block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Hash]
|
46
|
+
def actions
|
47
|
+
@actions || {}
|
48
|
+
end
|
49
|
+
|
50
|
+
# HTTP header that is searched for token
|
51
|
+
# @param header [String, nil]
|
52
|
+
# @return [String]
|
53
|
+
def http_header(header = nil)
|
54
|
+
if header
|
55
|
+
@http_header = header
|
56
|
+
else
|
57
|
+
@http_header || 'X-HaveAPI-Auth-Token'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Query parameter searched for token
|
62
|
+
# @param param [Symbol]
|
63
|
+
# @return [Symbol]
|
64
|
+
def query_parameter(param = nil)
|
65
|
+
if param
|
66
|
+
@query_param = param
|
67
|
+
else
|
68
|
+
@query_param || :_auth_token
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def inherited(subclass)
|
73
|
+
# Default request
|
74
|
+
subclass.request do
|
75
|
+
input do
|
76
|
+
string :user, label: 'User', required: true
|
77
|
+
password :password, label: 'Password', required: true
|
78
|
+
end
|
79
|
+
|
80
|
+
handle do
|
81
|
+
raise NotImplementedError
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Default renew and revoke
|
86
|
+
%i(renew revoke).each do |name|
|
87
|
+
subclass.send(name) do
|
88
|
+
handle do
|
89
|
+
raise NotImplementedError
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize(server, v)
|
97
|
+
@server = server
|
98
|
+
@version = v
|
99
|
+
end
|
100
|
+
|
101
|
+
# Authenticate request by `token`
|
102
|
+
#
|
103
|
+
# Return user object or nil.
|
104
|
+
# If the token was created as auto-renewable, this method
|
105
|
+
# is responsible for its renewal.
|
106
|
+
# Must be implemented.
|
107
|
+
# @param request [Sinatra::Request]
|
108
|
+
# @param token [String]
|
109
|
+
# @return [Object, nil]
|
110
|
+
def find_user_by_token(request, token)
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|