apisonator 2.100.2.pre1 → 3.0.0.pre1
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/CHANGELOG.md +40 -0
- data/Gemfile.base +0 -1
- data/Gemfile.lock +7 -21
- data/Gemfile.on_prem.lock +7 -21
- data/lib/3scale/backend.rb +0 -1
- data/lib/3scale/backend/application.rb +22 -6
- data/lib/3scale/backend/configuration.rb +2 -3
- data/lib/3scale/backend/errors.rb +0 -36
- data/lib/3scale/backend/listener.rb +2 -83
- data/lib/3scale/backend/metric/collection.rb +1 -4
- data/lib/3scale/backend/storage_async/client.rb +3 -1
- data/lib/3scale/backend/storage_helpers.rb +7 -2
- data/lib/3scale/backend/transactor.rb +27 -14
- data/lib/3scale/backend/transactor/report_job.rb +1 -1
- data/lib/3scale/backend/transactor/status.rb +0 -5
- data/lib/3scale/backend/usage_limit.rb +20 -11
- data/lib/3scale/backend/version.rb +1 -1
- data/licenses.xml +6 -66
- metadata +2 -9
- data/lib/3scale/backend/oauth.rb +0 -4
- data/lib/3scale/backend/oauth/token.rb +0 -26
- data/lib/3scale/backend/oauth/token_key.rb +0 -30
- data/lib/3scale/backend/oauth/token_storage.rb +0 -313
- data/lib/3scale/backend/oauth/token_value.rb +0 -25
- data/lib/3scale/backend/views/oauth_access_tokens.builder +0 -14
- data/lib/3scale/backend/views/oauth_app_id_by_token.builder +0 -4
@@ -75,10 +75,7 @@ module ThreeScale
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def load_metric_id(name)
|
78
|
-
|
79
|
-
:load_metric_id, @service_id, name)) do
|
80
|
-
storage.get(encode_key("metric/service_id:#{@service_id}/name:#{name}/id"))
|
81
|
-
end || raise(MetricInvalid.new(name))
|
78
|
+
Metric.load_id(@service_id, name) || raise(MetricInvalid.new(name))
|
82
79
|
end
|
83
80
|
|
84
81
|
## accepts postive integers or positive integers preffixed with # (for sets)
|
@@ -46,7 +46,9 @@ module ThreeScale
|
|
46
46
|
port ||= DEFAULT_PORT
|
47
47
|
|
48
48
|
endpoint = Async::IO::Endpoint.tcp(host, port)
|
49
|
-
@redis_async = Async::Redis::Client.new(
|
49
|
+
@redis_async = Async::Redis::Client.new(
|
50
|
+
endpoint, connection_limit: opts[:max_connections]
|
51
|
+
)
|
50
52
|
@building_pipeline = false
|
51
53
|
end
|
52
54
|
|
@@ -52,13 +52,18 @@ module ThreeScale
|
|
52
52
|
# duplicated commands because of this setting.
|
53
53
|
reconnect_attempts: 1,
|
54
54
|
# use by default the C extension client
|
55
|
-
driver: :hiredis
|
55
|
+
driver: :hiredis,
|
56
|
+
# applies only to async mode. The sync library opens 1 connection
|
57
|
+
# per process.
|
58
|
+
max_connections: 10,
|
56
59
|
}.freeze
|
57
60
|
private_constant :CONN_OPTIONS
|
58
61
|
|
59
62
|
# CONN_WHITELIST - Connection options that can be specified in config
|
60
63
|
# Note: we don't expose reconnect_attempts until the bug above is fixed
|
61
|
-
CONN_WHITELIST = [
|
64
|
+
CONN_WHITELIST = [
|
65
|
+
:connect_timeout, :read_timeout, :write_timeout, :max_connections
|
66
|
+
].freeze
|
62
67
|
private_constant :CONN_WHITELIST
|
63
68
|
|
64
69
|
# Parameters regarding target server we will take from a config object
|
@@ -65,19 +65,7 @@ module ThreeScale
|
|
65
65
|
params[:app_id] = nil if app_id && app_id.empty?
|
66
66
|
|
67
67
|
if oauth
|
68
|
-
if app_id.nil?
|
69
|
-
access_token = params[:access_token]
|
70
|
-
access_token = nil if access_token && access_token.empty?
|
71
|
-
|
72
|
-
if access_token.nil?
|
73
|
-
raise ApplicationNotFound.new nil if app_id.nil?
|
74
|
-
else
|
75
|
-
app_id = get_token_ids(access_token, service_id, app_id)
|
76
|
-
# update params, since they are checked elsewhere
|
77
|
-
params[:app_id] = app_id
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
68
|
+
raise ApplicationNotFound.new nil if app_id.nil?
|
81
69
|
validators = Validators::OAUTH_VALIDATORS
|
82
70
|
else
|
83
71
|
validators = Validators::VALIDATORS
|
@@ -87,9 +75,13 @@ module ThreeScale
|
|
87
75
|
application = Application.load_by_id_or_user_key!(service_id,
|
88
76
|
app_id,
|
89
77
|
params[:user_key])
|
78
|
+
|
79
|
+
extensions = request_info && request_info[:extensions] || {}
|
80
|
+
|
81
|
+
preload_usage_limits(application, extensions[:no_body], params[:usage])
|
82
|
+
|
90
83
|
now = Time.now.getutc
|
91
84
|
usage_values = Usage.application_usage(application, now)
|
92
|
-
extensions = request_info && request_info[:extensions] || {}
|
93
85
|
status_attrs = {
|
94
86
|
service_id: service_id,
|
95
87
|
application: application,
|
@@ -169,6 +161,27 @@ module ThreeScale
|
|
169
161
|
Resque.enqueue(ReportJob, service_id, data, Time.now.getutc.to_f, context_info)
|
170
162
|
end
|
171
163
|
|
164
|
+
# Loads the usage limits that are needed to authorize the current request.
|
165
|
+
# These are the cases:
|
166
|
+
# - When no_body is enabled, we only need to load the limits related with
|
167
|
+
# the metrics included in the request, that is, the ones included plus all
|
168
|
+
# their ancestors in the hierarchy. That's all we need to verify if the
|
169
|
+
# request is within the limits defined.
|
170
|
+
# - When no_body is disabled, we need to load all the limits because they
|
171
|
+
# are needed to generate the XML response.
|
172
|
+
# - When the usage reported in the request is empty, apisonator returns
|
173
|
+
# "limits_exceeded" if any of the limits defined has been violated. That's
|
174
|
+
# why in that scenario we need to load all the limits.
|
175
|
+
def preload_usage_limits(application, no_body_enabled, usage_in_params)
|
176
|
+
metric_names_in_usage = usage_in_params&.keys || {}
|
177
|
+
|
178
|
+
if no_body_enabled && !metric_names_in_usage.empty?
|
179
|
+
application.load_usage_limits_affected_by(metric_names_in_usage)
|
180
|
+
else
|
181
|
+
application.load_all_usage_limits
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
172
185
|
def storage
|
173
186
|
Storage.instance
|
174
187
|
end
|
@@ -59,7 +59,7 @@ module ThreeScale
|
|
59
59
|
return [] if transactions.empty?
|
60
60
|
transactions = transactions.values if transactions.respond_to?(:values)
|
61
61
|
transactions.group_by do |transaction|
|
62
|
-
Application.extract_id!(service_id, transaction['app_id'], transaction['user_key']
|
62
|
+
Application.extract_id!(service_id, transaction['app_id'], transaction['user_key'])
|
63
63
|
end.each(&block)
|
64
64
|
end
|
65
65
|
|
@@ -80,11 +80,6 @@ module ThreeScale
|
|
80
80
|
values && values[usage_limit.metric_id] || 0
|
81
81
|
end
|
82
82
|
|
83
|
-
def value_for_application_usage_limit(usage_limit)
|
84
|
-
values = @values[usage_limit.period]
|
85
|
-
values && values[usage_limit.metric_id] || 0
|
86
|
-
end
|
87
|
-
|
88
83
|
# provides a hierarchy hash with metrics as symbolic names
|
89
84
|
def hierarchy
|
90
85
|
@hierarchy ||= Metric.hierarchy service_id
|
@@ -22,20 +22,15 @@ module ThreeScale
|
|
22
22
|
|
23
23
|
def load_all(service_id, plan_id)
|
24
24
|
metric_ids = Metric.load_all_ids(service_id)
|
25
|
-
|
26
|
-
|
27
|
-
results = []
|
28
|
-
with_pairs_and_values service_id, plan_id, metric_ids do |pair, value|
|
29
|
-
value and results << new(service_id: service_id,
|
30
|
-
plan_id: plan_id,
|
31
|
-
metric_id: pair[0],
|
32
|
-
period: pair[1],
|
33
|
-
value: value.to_i)
|
34
|
-
end
|
35
|
-
results
|
25
|
+
generate_for_metrics(service_id, plan_id, metric_ids)
|
36
26
|
end
|
37
27
|
memoize :load_all
|
38
28
|
|
29
|
+
def load_for_affecting_metrics(service_id, plan_id, metric_ids)
|
30
|
+
generate_for_metrics(service_id, plan_id, metric_ids)
|
31
|
+
end
|
32
|
+
memoize :load_for_affecting_metrics
|
33
|
+
|
39
34
|
def load_value(service_id, plan_id, metric_id, period)
|
40
35
|
raw_value = storage.get(key(service_id, plan_id, metric_id, period))
|
41
36
|
raw_value and raw_value.to_i
|
@@ -80,6 +75,20 @@ module ThreeScale
|
|
80
75
|
encode_key(key_pre + period.to_s)
|
81
76
|
end
|
82
77
|
|
78
|
+
def generate_for_metrics(service_id, plan_id, metric_ids)
|
79
|
+
return metric_ids if metric_ids.empty?
|
80
|
+
|
81
|
+
results = []
|
82
|
+
with_pairs_and_values service_id, plan_id, metric_ids do |pair, value|
|
83
|
+
value and results << new(service_id: service_id,
|
84
|
+
plan_id: plan_id,
|
85
|
+
metric_id: pair[0],
|
86
|
+
period: pair[1],
|
87
|
+
value: value.to_i)
|
88
|
+
end
|
89
|
+
results
|
90
|
+
end
|
91
|
+
|
83
92
|
# yields [pair(metric_id, period), value]
|
84
93
|
def with_pairs_and_values(service_id, plan_id, metric_ids, &blk)
|
85
94
|
pairs, values = get_pairs_and_values_for service_id, plan_id, metric_ids
|
data/licenses.xml
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
<dependencies>
|
4
4
|
<dependency>
|
5
5
|
<packageName>activesupport</packageName>
|
6
|
-
<version>5.
|
6
|
+
<version>5.2.4.3</version>
|
7
7
|
<licenses>
|
8
8
|
<license>
|
9
9
|
<name>MIT</name>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
</dependency>
|
24
24
|
<dependency>
|
25
25
|
<packageName>apisonator</packageName>
|
26
|
-
<version>2.
|
26
|
+
<version>2.101.1</version>
|
27
27
|
<licenses>
|
28
28
|
<license>
|
29
29
|
<name>Apache 2.0</name>
|
@@ -243,7 +243,7 @@
|
|
243
243
|
</dependency>
|
244
244
|
<dependency>
|
245
245
|
<packageName>concurrent-ruby</packageName>
|
246
|
-
<version>1.
|
246
|
+
<version>1.1.6</version>
|
247
247
|
<licenses>
|
248
248
|
<license>
|
249
249
|
<name>MIT</name>
|
@@ -318,16 +318,6 @@
|
|
318
318
|
<url>http://opensource.org/licenses/mit-license</url>
|
319
319
|
</license>
|
320
320
|
</licenses>
|
321
|
-
</dependency>
|
322
|
-
<dependency>
|
323
|
-
<packageName>faraday</packageName>
|
324
|
-
<version>0.13.1</version>
|
325
|
-
<licenses>
|
326
|
-
<license>
|
327
|
-
<name>MIT</name>
|
328
|
-
<url>http://opensource.org/licenses/mit-license</url>
|
329
|
-
</license>
|
330
|
-
</licenses>
|
331
321
|
</dependency>
|
332
322
|
<dependency>
|
333
323
|
<packageName>ffi</packageName>
|
@@ -338,16 +328,6 @@
|
|
338
328
|
<url>http://opensource.org/licenses/BSD-3-Clause</url>
|
339
329
|
</license>
|
340
330
|
</licenses>
|
341
|
-
</dependency>
|
342
|
-
<dependency>
|
343
|
-
<packageName>geminabox</packageName>
|
344
|
-
<version>0.13.11</version>
|
345
|
-
<licenses>
|
346
|
-
<license>
|
347
|
-
<name>MIT-LICENSE</name>
|
348
|
-
<url></url>
|
349
|
-
</license>
|
350
|
-
</licenses>
|
351
331
|
</dependency>
|
352
332
|
<dependency>
|
353
333
|
<packageName>gli</packageName>
|
@@ -368,20 +348,10 @@
|
|
368
348
|
<url>http://opensource.org/licenses/BSD-3-Clause</url>
|
369
349
|
</license>
|
370
350
|
</licenses>
|
371
|
-
</dependency>
|
372
|
-
<dependency>
|
373
|
-
<packageName>httpclient</packageName>
|
374
|
-
<version>2.8.3</version>
|
375
|
-
<licenses>
|
376
|
-
<license>
|
377
|
-
<name>ruby</name>
|
378
|
-
<url>http://www.ruby-lang.org/en/LICENSE.txt</url>
|
379
|
-
</license>
|
380
|
-
</licenses>
|
381
351
|
</dependency>
|
382
352
|
<dependency>
|
383
353
|
<packageName>i18n</packageName>
|
384
|
-
<version>
|
354
|
+
<version>1.8.2</version>
|
385
355
|
<licenses>
|
386
356
|
<license>
|
387
357
|
<name>MIT</name>
|
@@ -471,7 +441,7 @@
|
|
471
441
|
</dependency>
|
472
442
|
<dependency>
|
473
443
|
<packageName>minitest</packageName>
|
474
|
-
<version>5.
|
444
|
+
<version>5.14.1</version>
|
475
445
|
<licenses>
|
476
446
|
<license>
|
477
447
|
<name>MIT</name>
|
@@ -512,16 +482,6 @@
|
|
512
482
|
<url>http://opensource.org/licenses/mit-license</url>
|
513
483
|
</license>
|
514
484
|
</licenses>
|
515
|
-
</dependency>
|
516
|
-
<dependency>
|
517
|
-
<packageName>multipart-post</packageName>
|
518
|
-
<version>2.0.0</version>
|
519
|
-
<licenses>
|
520
|
-
<license>
|
521
|
-
<name>MIT</name>
|
522
|
-
<url>http://opensource.org/licenses/mit-license</url>
|
523
|
-
</license>
|
524
|
-
</licenses>
|
525
485
|
</dependency>
|
526
486
|
<dependency>
|
527
487
|
<packageName>mustache</packageName>
|
@@ -542,16 +502,6 @@
|
|
542
502
|
<url>http://opensource.org/licenses/mit-license</url>
|
543
503
|
</license>
|
544
504
|
</licenses>
|
545
|
-
</dependency>
|
546
|
-
<dependency>
|
547
|
-
<packageName>nesty</packageName>
|
548
|
-
<version>1.0.2</version>
|
549
|
-
<licenses>
|
550
|
-
<license>
|
551
|
-
<name>MIT</name>
|
552
|
-
<url>http://opensource.org/licenses/mit-license</url>
|
553
|
-
</license>
|
554
|
-
</licenses>
|
555
505
|
</dependency>
|
556
506
|
<dependency>
|
557
507
|
<packageName>net-scp</packageName>
|
@@ -826,16 +776,6 @@
|
|
826
776
|
<url>http://opensource.org/licenses/mit-license</url>
|
827
777
|
</license>
|
828
778
|
</licenses>
|
829
|
-
</dependency>
|
830
|
-
<dependency>
|
831
|
-
<packageName>reentrant_flock</packageName>
|
832
|
-
<version>0.1.1</version>
|
833
|
-
<licenses>
|
834
|
-
<license>
|
835
|
-
<name>MIT</name>
|
836
|
-
<url>http://opensource.org/licenses/mit-license</url>
|
837
|
-
</license>
|
838
|
-
</licenses>
|
839
779
|
</dependency>
|
840
780
|
<dependency>
|
841
781
|
<packageName>resque</packageName>
|
@@ -1123,7 +1063,7 @@
|
|
1123
1063
|
</dependency>
|
1124
1064
|
<dependency>
|
1125
1065
|
<packageName>tzinfo</packageName>
|
1126
|
-
<version>1.2.
|
1066
|
+
<version>1.2.7</version>
|
1127
1067
|
<licenses>
|
1128
1068
|
<license>
|
1129
1069
|
<name>MIT</name>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apisonator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0.pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Ciganek
|
@@ -16,7 +16,7 @@ authors:
|
|
16
16
|
autorequire:
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
|
-
date: 2020-
|
19
|
+
date: 2020-06-19 00:00:00.000000000 Z
|
20
20
|
dependencies: []
|
21
21
|
description: This gem provides a daemon that handles authorization and reporting of
|
22
22
|
web services managed by 3scale.
|
@@ -111,11 +111,6 @@ files:
|
|
111
111
|
- lib/3scale/backend/memoizer.rb
|
112
112
|
- lib/3scale/backend/metric.rb
|
113
113
|
- lib/3scale/backend/metric/collection.rb
|
114
|
-
- lib/3scale/backend/oauth.rb
|
115
|
-
- lib/3scale/backend/oauth/token.rb
|
116
|
-
- lib/3scale/backend/oauth/token_key.rb
|
117
|
-
- lib/3scale/backend/oauth/token_storage.rb
|
118
|
-
- lib/3scale/backend/oauth/token_value.rb
|
119
114
|
- lib/3scale/backend/period.rb
|
120
115
|
- lib/3scale/backend/period/boundary.rb
|
121
116
|
- lib/3scale/backend/period/cache.rb
|
@@ -184,8 +179,6 @@ files:
|
|
184
179
|
- lib/3scale/backend/validators/service_state.rb
|
185
180
|
- lib/3scale/backend/validators/state.rb
|
186
181
|
- lib/3scale/backend/version.rb
|
187
|
-
- lib/3scale/backend/views/oauth_access_tokens.builder
|
188
|
-
- lib/3scale/backend/views/oauth_app_id_by_token.builder
|
189
182
|
- lib/3scale/backend/worker.rb
|
190
183
|
- lib/3scale/backend/worker_async.rb
|
191
184
|
- lib/3scale/backend/worker_metrics.rb
|
data/lib/3scale/backend/oauth.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module ThreeScale
|
2
|
-
module Backend
|
3
|
-
module OAuth
|
4
|
-
class Token
|
5
|
-
attr_reader :service_id, :token, :key
|
6
|
-
attr_accessor :ttl, :app_id
|
7
|
-
|
8
|
-
def initialize(token, service_id, app_id, ttl)
|
9
|
-
@token = token
|
10
|
-
@service_id = service_id
|
11
|
-
@app_id = app_id
|
12
|
-
@ttl = ttl
|
13
|
-
@key = Key.for token, service_id
|
14
|
-
end
|
15
|
-
|
16
|
-
def value
|
17
|
-
Value.for app_id
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.from_value(token, service_id, value, ttl)
|
21
|
-
new token, service_id, *Value.from(value), ttl
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# This module defines the format of the keys for OAuth tokens and token sets.
|
2
|
-
#
|
3
|
-
# Note that while we can build the key easily, we cannot reliably obtain a token
|
4
|
-
# and a service_id out of the key, because there are no constraints on them:
|
5
|
-
#
|
6
|
-
# "oauth_access_tokens/service:some/servicegoeshere/andthisis_a_/valid_token"
|
7
|
-
#
|
8
|
-
module ThreeScale
|
9
|
-
module Backend
|
10
|
-
module OAuth
|
11
|
-
class Token
|
12
|
-
module Key
|
13
|
-
class << self
|
14
|
-
def for(token, service_id)
|
15
|
-
"oauth_access_tokens/service:#{service_id}/#{token}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module Set
|
20
|
-
class << self
|
21
|
-
def for(service_id, app_id)
|
22
|
-
"oauth_access_tokens/service:#{service_id}/app:#{app_id}/"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,313 +0,0 @@
|
|
1
|
-
module ThreeScale
|
2
|
-
module Backend
|
3
|
-
module OAuth
|
4
|
-
class Token
|
5
|
-
module Storage
|
6
|
-
include Configurable
|
7
|
-
|
8
|
-
# Default token size is 4K - 512 (to allow for some metadata)
|
9
|
-
MAXIMUM_TOKEN_SIZE = configuration.oauth.max_token_size || 3584
|
10
|
-
private_constant :MAXIMUM_TOKEN_SIZE
|
11
|
-
TOKEN_MAX_REDIS_SLICE_SIZE = 500
|
12
|
-
private_constant :TOKEN_MAX_REDIS_SLICE_SIZE
|
13
|
-
TOKEN_TTL_DEFAULT = 86400
|
14
|
-
private_constant :TOKEN_TTL_DEFAULT
|
15
|
-
TOKEN_TTL_PERMANENT = 0
|
16
|
-
private_constant :TOKEN_TTL_PERMANENT
|
17
|
-
|
18
|
-
Error = Class.new StandardError
|
19
|
-
InconsistencyError = Class.new Error
|
20
|
-
|
21
|
-
class << self
|
22
|
-
include Backend::Logging
|
23
|
-
include Backend::StorageHelpers
|
24
|
-
|
25
|
-
def create(token, service_id, app_id, ttl = nil)
|
26
|
-
raise AccessTokenFormatInvalid if token.nil? || token.empty? ||
|
27
|
-
!token.is_a?(String) || token.bytesize > MAXIMUM_TOKEN_SIZE
|
28
|
-
|
29
|
-
# raises if TTL is invalid
|
30
|
-
ttl = sanitized_ttl ttl
|
31
|
-
|
32
|
-
key = Key.for token, service_id
|
33
|
-
raise AccessTokenAlreadyExists.new(token) unless storage.get(key).nil?
|
34
|
-
|
35
|
-
value = Value.for app_id
|
36
|
-
token_set = Key::Set.for(service_id, app_id)
|
37
|
-
|
38
|
-
store_token token, token_set, key, value, ttl
|
39
|
-
ensure_stored! token, token_set, key, value
|
40
|
-
end
|
41
|
-
|
42
|
-
# Deletes a token
|
43
|
-
#
|
44
|
-
# Returns the associated app_id or nil
|
45
|
-
#
|
46
|
-
def delete(token, service_id)
|
47
|
-
key = Key.for token, service_id
|
48
|
-
val = storage.get key
|
49
|
-
if val
|
50
|
-
app_id = Value.from val
|
51
|
-
token_set = Key::Set.for(service_id, app_id)
|
52
|
-
|
53
|
-
existed, * = remove_a_token token_set, token, key
|
54
|
-
|
55
|
-
unless existed
|
56
|
-
logger.notify(InconsistencyError.new("Found OAuth token " \
|
57
|
-
"#{token} for service #{service_id} and app #{app_id} as " \
|
58
|
-
"key but not in set!"))
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
val
|
63
|
-
end
|
64
|
-
|
65
|
-
# Get a token's associated app_id
|
66
|
-
def get_credentials(token, service_id)
|
67
|
-
app_id = Value.from(storage.get(Key.for(token, service_id)))
|
68
|
-
raise AccessTokenInvalid.new token if app_id.nil?
|
69
|
-
app_id
|
70
|
-
end
|
71
|
-
|
72
|
-
# This is used to list tokens by service and app.
|
73
|
-
#
|
74
|
-
# Note: this deletes tokens that have not been found from the set of
|
75
|
-
# tokens for the given app - those have to be expired tokens.
|
76
|
-
def all_by_service_and_app(service_id, app_id)
|
77
|
-
token_set = Key::Set.for(service_id, app_id)
|
78
|
-
deltokens = []
|
79
|
-
tokens_n_values_flat(token_set, service_id)
|
80
|
-
.select do |(token, _key, value, _ttl)|
|
81
|
-
app_id = Value.from value
|
82
|
-
if app_id.nil?
|
83
|
-
deltokens << token
|
84
|
-
false
|
85
|
-
else
|
86
|
-
true
|
87
|
-
end
|
88
|
-
end
|
89
|
-
.map do |(token, _key, value, ttl)|
|
90
|
-
Token.from_value token, service_id, value, ttl
|
91
|
-
end
|
92
|
-
.tap do
|
93
|
-
# delete expired tokens (nil values) from token set
|
94
|
-
deltokens.each_slice(TOKEN_MAX_REDIS_SLICE_SIZE) do |delgrp|
|
95
|
-
storage.srem token_set, delgrp
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# Remove tokens by app_id.
|
101
|
-
#
|
102
|
-
# Triggered by Application deletion.
|
103
|
-
#
|
104
|
-
def remove_tokens(service_id, app_id)
|
105
|
-
remove_tokens_by service_id, app_id
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
# Remove all tokens
|
112
|
-
#
|
113
|
-
# I thought of leaving this one public, but remove_*_tokens removed
|
114
|
-
# my use cases for the time being.
|
115
|
-
def remove_tokens_by(service_id, app_id)
|
116
|
-
token_set = Key::Set.for(service_id, app_id)
|
117
|
-
|
118
|
-
remove_whole_token_set(token_set, service_id)
|
119
|
-
end
|
120
|
-
|
121
|
-
def remove_token_set_by(token_set, service_id, &blk)
|
122
|
-
# Get tokens. Filter them. Group them into manageable groups.
|
123
|
-
# Extract tokens and keys into separate arrays, one for each.
|
124
|
-
# Remove tokens from token set (they are keys in a set) and token
|
125
|
-
# keys themselves.
|
126
|
-
tokens_n_values_flat(token_set, service_id, false)
|
127
|
-
.select(&blk)
|
128
|
-
.each_slice(TOKEN_MAX_REDIS_SLICE_SIZE)
|
129
|
-
.inject([[], []]) do |acc, groups|
|
130
|
-
groups.each do |token, key, _value|
|
131
|
-
acc[0] << token
|
132
|
-
acc[1] << key
|
133
|
-
end
|
134
|
-
acc
|
135
|
-
end
|
136
|
-
.each_slice(2)
|
137
|
-
.inject([]) do |acc, (tokens, keys)|
|
138
|
-
storage.pipelined do
|
139
|
-
if tokens && !tokens.empty?
|
140
|
-
storage.srem token_set, tokens
|
141
|
-
acc.concat tokens
|
142
|
-
end
|
143
|
-
storage.del keys if keys && !keys.empty?
|
144
|
-
end
|
145
|
-
acc
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def remove_a_token(token_set, token, key)
|
150
|
-
storage.pipelined do
|
151
|
-
storage.srem token_set, token
|
152
|
-
storage.del key
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def remove_whole_token_set(token_set, service_id)
|
157
|
-
_token_groups, key_groups = tokens_n_keys(token_set, service_id)
|
158
|
-
storage.pipelined do
|
159
|
-
storage.del token_set
|
160
|
-
# remove all tokens for this app
|
161
|
-
key_groups.each do |keys|
|
162
|
-
storage.del keys
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
# TODO: provide a SSCAN interface with lazy enums because SMEMBERS
|
168
|
-
# is prone to DoSing and timeouts
|
169
|
-
def tokens_from(token_set)
|
170
|
-
storage.smembers(token_set)
|
171
|
-
end
|
172
|
-
|
173
|
-
def tokens_n_keys(token_set, service_id)
|
174
|
-
token_groups = tokens_from(token_set).each_slice(TOKEN_MAX_REDIS_SLICE_SIZE)
|
175
|
-
key_groups = token_groups.map do |tokens|
|
176
|
-
tokens.map do |token|
|
177
|
-
Key.for token, service_id
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
[token_groups, key_groups]
|
182
|
-
end
|
183
|
-
|
184
|
-
# Provides grouped data which matches respectively in each array
|
185
|
-
# position, ie. 1st group of data contains a group of tokens, keys
|
186
|
-
# and values with ttls, and position N of the tokens group has key
|
187
|
-
# in position N of the keys group, and so on.
|
188
|
-
#
|
189
|
-
# [[[token group], [key group], [value_with_ttls_group]], ...]
|
190
|
-
#
|
191
|
-
def tokens_n_values_groups(token_set, service_id, with_ttls)
|
192
|
-
token_groups, key_groups = tokens_n_keys(token_set, service_id)
|
193
|
-
value_ttl_groups = key_groups.map do |keys|
|
194
|
-
# pipelining will create an array with the results of commands
|
195
|
-
res = storage.pipelined do
|
196
|
-
storage.mget(keys)
|
197
|
-
if with_ttls
|
198
|
-
keys.map do |key|
|
199
|
-
storage.ttl key
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
# [mget array, 0..n ttls] => [mget array, ttls array]
|
204
|
-
[res.shift, res]
|
205
|
-
end
|
206
|
-
token_groups.zip(key_groups, value_ttl_groups)
|
207
|
-
end
|
208
|
-
|
209
|
-
# Zips the data provided by tokens_n_values_groups so that you stop
|
210
|
-
# looking at indexes in the respective arrays and instead have:
|
211
|
-
#
|
212
|
-
# [group 0, ..., group N] where each group is made of:
|
213
|
-
# [[token 0, key 0, value 0, ttl 0], ..., [token N, key N, value
|
214
|
-
# N, ttl N]]
|
215
|
-
#
|
216
|
-
def tokens_n_values_zipped_groups(token_set, service_id, with_ttls = true)
|
217
|
-
tokens_n_values_groups(token_set,
|
218
|
-
service_id,
|
219
|
-
with_ttls).map do |tokens, keys, (values, ttls)|
|
220
|
-
tokens.zip keys, values, ttls
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# Flattens the data provided by tokens_n_values_zipped_groups so
|
225
|
-
# that you have a transparent iterator with all needed data and can
|
226
|
-
# stop worrying about streaming groups of elements.
|
227
|
-
#
|
228
|
-
def tokens_n_values_flat(token_set, service_id, with_ttls = true)
|
229
|
-
tokens_n_values_zipped_groups(token_set,
|
230
|
-
service_id,
|
231
|
-
with_ttls).flat_map do |groups|
|
232
|
-
groups.map do |token, key, value, ttl|
|
233
|
-
[token, key, value, ttl]
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
# Store the specified token in Redis
|
239
|
-
#
|
240
|
-
# TTL specified in seconds.
|
241
|
-
# A TTL of 0 stores a permanent token
|
242
|
-
def store_token(token, token_set, key, value, ttl)
|
243
|
-
# build the storage command so that we can pipeline everything cleanly
|
244
|
-
command = :set
|
245
|
-
args = [key]
|
246
|
-
|
247
|
-
if !permanent_ttl? ttl
|
248
|
-
command = :setex
|
249
|
-
args << ttl
|
250
|
-
end
|
251
|
-
|
252
|
-
args << value
|
253
|
-
|
254
|
-
# pipelined will return nil if it is embedded into another
|
255
|
-
# pipeline(which would be an error at this point) or if shutting
|
256
|
-
# down and a connection error happens. Both things being abnormal
|
257
|
-
# means we should just raise a storage error.
|
258
|
-
raise AccessTokenStorageError, token unless storage.pipelined do
|
259
|
-
storage.send(command, *args)
|
260
|
-
storage.sadd(token_set, token)
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
|
265
|
-
# Make sure everything ended up there
|
266
|
-
#
|
267
|
-
# TODO: review and possibly reimplement trying to leave it
|
268
|
-
# consistent as much as possible.
|
269
|
-
#
|
270
|
-
# Note that we have a sharding proxy and pipelines can't be guaranteed
|
271
|
-
# to behave like transactions, since we might have one non-working
|
272
|
-
# shard. Instead of relying on proxy-specific responses, we just check
|
273
|
-
# that the data we should have in the store is really there.
|
274
|
-
def ensure_stored!(token, token_set, key, value)
|
275
|
-
results = storage.pipelined do
|
276
|
-
storage.get(key)
|
277
|
-
storage.sismember(token_set, token)
|
278
|
-
end
|
279
|
-
|
280
|
-
results.last && results.first == value ||
|
281
|
-
raise(AccessTokenStorageError, token)
|
282
|
-
end
|
283
|
-
|
284
|
-
# Validation for the TTL value
|
285
|
-
#
|
286
|
-
# 0 is accepted (understood as permanent token)
|
287
|
-
# Negative values are not accepted
|
288
|
-
# Integer(ttl) validation is required (if input is nil, default applies)
|
289
|
-
def sanitized_ttl(ttl)
|
290
|
-
ttl = begin
|
291
|
-
Integer(ttl)
|
292
|
-
rescue TypeError
|
293
|
-
# ttl is nil
|
294
|
-
TOKEN_TTL_DEFAULT
|
295
|
-
rescue
|
296
|
-
# NaN
|
297
|
-
-1
|
298
|
-
end
|
299
|
-
raise AccessTokenInvalidTTL if ttl < 0
|
300
|
-
|
301
|
-
ttl
|
302
|
-
end
|
303
|
-
|
304
|
-
# Check whether a TTL has the magic value for a permanent token
|
305
|
-
def permanent_ttl?(ttl)
|
306
|
-
ttl == TOKEN_TTL_PERMANENT
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|