zaikio-jwt_auth 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10365dcb96c417de5f8226e97fc835334e5b6ed92868fead27d443565013c7d3
4
- data.tar.gz: 5c66b0a5359ab1db156861650ccb142ec1e5572074000fb8c0c4c1740ccc833c
3
+ metadata.gz: 69257f2c9dcf661babfc6b5600c0b4b724c32b4e92ffb6743e47a560e020a81c
4
+ data.tar.gz: 0b61afe2f4587ba84ac3e922bcb67024b8ec9f53b69cdbf155c17b4c556388d6
5
5
  SHA512:
6
- metadata.gz: b52f0baddf61c6d418b0b82a1e003a82fde64484182485662111ec52a8d37275886db7c282ab9ab7d211888eb64c760d2541662905200e80f016c13dcb04566a
7
- data.tar.gz: 815b388a900b8b3f6d10a46f9a0968f6c51c7227fe232ef44ecb2509f4c23d9dbea34482de4f9229af6de455e98bf3d31529ab86618d30685dcfcae2e4cc6881
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
- json = Oj.load(cache) if cache
34
+ return reload_or_enqueue(directory_path) unless cache
22
35
 
23
- if !cache || options[:invalidate] || cache_expired?(json, options[:expires_after])
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["data"]
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 = {})
@@ -30,7 +30,7 @@ module Zaikio
30
30
  def keys
31
31
  return Zaikio::JWTAuth.configuration.keys if Zaikio::JWTAuth.configuration.keys
32
32
 
33
- fetch_from_cache["keys"]
33
+ fetch_from_cache.fetch("keys")
34
34
  end
35
35
 
36
36
  def fetch_from_cache(options = {})
@@ -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?(scope_configurations, action_name, context) # rubocop:disable Metrics/AbcSize
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 action_matches_config?(scope_configuration, action_name)
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(".")
@@ -1,5 +1,5 @@
1
1
  module Zaikio
2
2
  module JWTAuth
3
- VERSION = "2.2.0".freeze
3
+ VERSION = "2.3.0".freeze
4
4
  end
5
5
  end
@@ -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 || DirectoryCache.fetch(
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
- )["revoked_token_ids"]
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
- self.class.authorize_by_jwt_scopes,
131
- action_name,
132
- self
175
+ configuration,
176
+ action_name
133
177
  )
134
178
 
135
- render_error("unpermitted_scope")
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" => [error] })
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.2.0
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: 2022-09-28 00:00:00.000000000 Z
13
+ date: 2023-02-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activejob