zaikio-jwt_auth 2.2.0 → 2.3.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/lib/zaikio/jwt_auth/directory_cache.rb +18 -6
- data/lib/zaikio/jwt_auth/jwk.rb +1 -1
- data/lib/zaikio/jwt_auth/token_data.rb +3 -24
- data/lib/zaikio/jwt_auth/version.rb +1 -1
- data/lib/zaikio/jwt_auth.rb +62 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69257f2c9dcf661babfc6b5600c0b4b724c32b4e92ffb6743e47a560e020a81c
|
4
|
+
data.tar.gz: 0b61afe2f4587ba84ac3e922bcb67024b8ec9f53b69cdbf155c17b4c556388d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7717ef3559a647592cca0568e09531fdd8854c61d2dcdfb64077faec8bf0bd31e3dc87336b62dd0fa6b6673cbc6de4dfc5fb94d3371e2c9598e295d18e4d8578
|
7
|
+
data.tar.gz: 82094718a024d29a023220560517c77d59bc29b7d4dc293419b23ee9204bd936f6d14e76c4b2698c95a71ca0f72bddbb6bc2387d70b801f85da86b2ad2b86b5f
|
@@ -15,17 +15,29 @@ module Zaikio
|
|
15
15
|
BadResponseError = Class.new(StandardError)
|
16
16
|
|
17
17
|
class << self
|
18
|
+
# Retrieve some data from the Hub, reachable at `directory_path`. Attempts to
|
19
|
+
# retrieve data from a cache first (usually Redis, if configured). Caching can be
|
20
|
+
# skipped by setting an `:invalidate` option, or if the cached data is stale it
|
21
|
+
# will be refetched from the Hub anyway. Please note that this method can return
|
22
|
+
# `nil` if there is no cache available and the Hub is giving error responses or
|
23
|
+
# failures.
|
24
|
+
#
|
25
|
+
# @example Fetching revoked access token information
|
26
|
+
#
|
27
|
+
# DirectoryCache.fetch("api/v1/revoked_access_tokens.json")
|
28
|
+
#
|
29
|
+
# @returns Hash (in the happy path)
|
30
|
+
# @returns nil (if the cache is unavailable and the API is down)
|
18
31
|
def fetch(directory_path, options = {})
|
19
32
|
cache = Zaikio::JWTAuth.configuration.cache.read("zaikio::jwt_auth::#{directory_path}")
|
20
33
|
|
21
|
-
|
34
|
+
return reload_or_enqueue(directory_path) unless cache
|
22
35
|
|
23
|
-
|
24
|
-
new_values = reload_or_enqueue(directory_path)
|
25
|
-
return new_values || json["data"]
|
26
|
-
end
|
36
|
+
json = Oj.load(cache)
|
27
37
|
|
28
|
-
json[
|
38
|
+
if options[:invalidate] || cache_expired?(json, options[:expires_after])
|
39
|
+
reload_or_enqueue(directory_path)
|
40
|
+
end || json["data"]
|
29
41
|
end
|
30
42
|
|
31
43
|
def update(directory_path, options = {})
|
data/lib/zaikio/jwt_auth/jwk.rb
CHANGED
@@ -47,36 +47,15 @@ module Zaikio
|
|
47
47
|
|
48
48
|
# scope_options is an array of objects with:
|
49
49
|
# scope, app_name (optional), except/only (array, optional), type (read, write, readwrite)
|
50
|
-
def scope_by_configurations?(
|
51
|
-
configuration = scope_configurations.find do |scope_configuration|
|
52
|
-
action_matches = action_matches_config?(scope_configuration, action_name)
|
53
|
-
|
54
|
-
if action_matches && scope_configuration[:if] && !context.instance_exec(&scope_configuration[:if])
|
55
|
-
false
|
56
|
-
elsif action_matches && scope_configuration[:unless] && context.instance_exec(&scope_configuration[:unless])
|
57
|
-
false
|
58
|
-
else
|
59
|
-
action_matches
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
50
|
+
def scope_by_configurations?(configuration, action_name)
|
63
51
|
return true unless configuration
|
64
52
|
|
65
53
|
scope?(configuration[:scopes], action_name, app_name: configuration[:app_name], type: configuration[:type])
|
66
54
|
end
|
67
55
|
|
68
|
-
def
|
69
|
-
if scope_configuration[:only]
|
70
|
-
Array(scope_configuration[:only]).any? { |a| a.to_s == action_name }
|
71
|
-
elsif scope_configuration[:except]
|
72
|
-
Array(scope_configuration[:except]).none? { |a| a.to_s == action_name }
|
73
|
-
else
|
74
|
-
true
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def scope?(allowed_scopes, action_name, app_name: nil, type: nil)
|
56
|
+
def scope?(allowed_scopes, action_name, app_name: nil, type: nil, scope: nil) # rubocop:disable Metrics/AbcSize
|
79
57
|
app_name ||= Zaikio::JWTAuth.configuration.app_name
|
58
|
+
scope ||= self.scope
|
80
59
|
Array(allowed_scopes).map(&:to_s).any? do |allowed_scope|
|
81
60
|
scope.any? do |s|
|
82
61
|
parts = s.split(".")
|
data/lib/zaikio/jwt_auth.rb
CHANGED
@@ -12,6 +12,8 @@ require "zaikio/jwt_auth/test_helper"
|
|
12
12
|
|
13
13
|
module Zaikio
|
14
14
|
module JWTAuth
|
15
|
+
DOCS_LINK = "For more information check our docs: https://docs.zaikio.com/guide/oauth/scopes.html".freeze
|
16
|
+
|
15
17
|
class << self
|
16
18
|
attr_accessor :configuration
|
17
19
|
end
|
@@ -34,10 +36,14 @@ module Zaikio
|
|
34
36
|
def self.revoked_token_ids
|
35
37
|
return [] if mocked_jwt_payload
|
36
38
|
|
37
|
-
configuration.revoked_token_ids
|
39
|
+
return configuration.revoked_token_ids if configuration.revoked_token_ids
|
40
|
+
|
41
|
+
result = DirectoryCache.fetch(
|
38
42
|
"api/v1/revoked_access_tokens.json",
|
39
43
|
expires_after: 60.minutes
|
40
|
-
)
|
44
|
+
) || {}
|
45
|
+
|
46
|
+
result.fetch("revoked_token_ids", [])
|
41
47
|
end
|
42
48
|
|
43
49
|
def self.included(base)
|
@@ -125,14 +131,61 @@ module Zaikio
|
|
125
131
|
|
126
132
|
private
|
127
133
|
|
134
|
+
def find_scope_configuration(scope_configurations)
|
135
|
+
scope_configurations.find do |scope_configuration|
|
136
|
+
action_matches = action_matches_config?(scope_configuration)
|
137
|
+
|
138
|
+
if action_matches && scope_configuration[:if] && !instance_exec(&scope_configuration[:if])
|
139
|
+
false
|
140
|
+
elsif action_matches && scope_configuration[:unless] && instance_exec(&scope_configuration[:unless])
|
141
|
+
false
|
142
|
+
else
|
143
|
+
action_matches
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def action_matches_config?(scope_configuration)
|
149
|
+
if scope_configuration[:only]
|
150
|
+
Array(scope_configuration[:only]).any? { |a| a.to_s == action_name }
|
151
|
+
elsif scope_configuration[:except]
|
152
|
+
Array(scope_configuration[:except]).none? { |a| a.to_s == action_name }
|
153
|
+
else
|
154
|
+
true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def required_scopes(token_data, configuration)
|
159
|
+
Array(configuration[:scopes]).flat_map do |allowed_scope|
|
160
|
+
%i[r w rw].filter_map do |type|
|
161
|
+
app_name = configuration[:app_name] || Zaikio::JWTAuth.configuration.app_name
|
162
|
+
full_scope = "#{app_name}.#{allowed_scope}.#{type}"
|
163
|
+
if token_data.scope?([allowed_scope], action_name, app_name: app_name, type: configuration[:type],
|
164
|
+
scope: [full_scope])
|
165
|
+
full_scope
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
128
171
|
def show_error_if_authorize_by_jwt_scopes_fails(token_data)
|
172
|
+
configuration = find_scope_configuration(self.class.authorize_by_jwt_scopes)
|
173
|
+
|
129
174
|
return if token_data.scope_by_configurations?(
|
130
|
-
|
131
|
-
action_name
|
132
|
-
self
|
175
|
+
configuration,
|
176
|
+
action_name
|
133
177
|
)
|
134
178
|
|
135
|
-
|
179
|
+
details = nil
|
180
|
+
|
181
|
+
if configuration
|
182
|
+
required_scopes = required_scopes(token_data, configuration)
|
183
|
+
|
184
|
+
details = "This endpoint requires one of the following scopes: #{required_scopes.join(', ')} but your " \
|
185
|
+
"access token only includes the following scopes: #{token_data.scope.join(', ')} - #{DOCS_LINK}"
|
186
|
+
end
|
187
|
+
|
188
|
+
render_error(["unpermitted_scope", details])
|
136
189
|
end
|
137
190
|
|
138
191
|
def show_error_if_authorize_by_jwt_subject_type_fails(token_data)
|
@@ -141,7 +194,8 @@ module Zaikio
|
|
141
194
|
return
|
142
195
|
end
|
143
196
|
|
144
|
-
render_error("unpermitted_subject"
|
197
|
+
render_error(["unpermitted_subject", "Expected Subject Type: #{self.class.authorize_by_jwt_subject_type} | "\
|
198
|
+
"Subject type from Access Token: #{token_data.subject_type} - #{DOCS_LINK}"])
|
145
199
|
end
|
146
200
|
|
147
201
|
def show_error_if_token_is_revoked(token_data)
|
@@ -151,7 +205,7 @@ module Zaikio
|
|
151
205
|
end
|
152
206
|
|
153
207
|
def render_error(error, status: :forbidden)
|
154
|
-
render(status: status, json: { "errors" =>
|
208
|
+
render(status: status, json: { "errors" => Array(error).compact })
|
155
209
|
end
|
156
210
|
|
157
211
|
def jwt_options
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zaikio-jwt_auth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- crispymtn
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2023-02-09 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activejob
|