verikloak-audience 0.2.6 → 0.2.7
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 +10 -0
- data/lib/generators/verikloak/audience/install/install_generator.rb +4 -6
- data/lib/generators/verikloak/audience/install/templates/initializer.rb.erb +7 -0
- data/lib/verikloak/audience/checker.rb +16 -9
- data/lib/verikloak/audience/configuration.rb +9 -12
- data/lib/verikloak/audience/middleware.rb +9 -13
- data/lib/verikloak/audience/railtie.rb +135 -24
- data/lib/verikloak/audience/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 59d9195c0dfe4835b91a16bf8f08a58369f398890b5aff664979421a388a91d1
|
|
4
|
+
data.tar.gz: 8bd59e610df30f6eb1238b06f12a2778ddb0287264a967424751db38acae2f81
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0d5d65f4ff5ca863d1f638b7e19a71edb256aa905bcc21b1ac49802b4c8db53697ed50d6f45bd67730a147a215972ddadbc78a403155e5d3b6ea1cf5b341dc69
|
|
7
|
+
data.tar.gz: c149176b446b3021204c039d2a07f9297676b57db52ae63f40812029b7f899c7398a425ba1a1542f058303e5a0615d51ebd793b5610d1c9feeb98bd7009c1c88
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.2.7] - 2025-09-27
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Rails Railtie now syncs `env_claims_key`, `required_aud`, and `resource_client` defaults with verikloak-rails configuration after boot.
|
|
14
|
+
- Generator template converted to ERB and bundled with the gem so `rails g verikloak:audience:install` produces the initializer without relying on Rails internals.
|
|
15
|
+
- Middleware option validation tightened to fail fast on unknown overrides with clearer error messages.
|
|
16
|
+
|
|
17
|
+
### Documentation
|
|
18
|
+
- Added RubyDoc comments across the Railtie to clarify initializer responsibilities and helper methods.
|
|
19
|
+
|
|
10
20
|
## [0.2.6] - 2025-09-23
|
|
11
21
|
|
|
12
22
|
### Added
|
|
@@ -4,7 +4,7 @@ begin
|
|
|
4
4
|
require 'rails/generators'
|
|
5
5
|
require 'rails/generators/base'
|
|
6
6
|
rescue LoadError
|
|
7
|
-
|
|
7
|
+
raise unless defined?(Rails::Generators::Base)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Verikloak
|
|
@@ -14,13 +14,11 @@ module Verikloak
|
|
|
14
14
|
# application. This generator creates an initializer that inserts the
|
|
15
15
|
# audience middleware after the core Verikloak middleware once it is
|
|
16
16
|
# available.
|
|
17
|
-
class InstallGenerator <
|
|
18
|
-
source_root File.expand_path('templates', __dir__)
|
|
17
|
+
class InstallGenerator < Rails::Generators::Base
|
|
18
|
+
source_root File.expand_path('templates', __dir__)
|
|
19
19
|
|
|
20
20
|
def create_initializer
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
template 'verikloak_audience.rb.tt', 'config/initializers/verikloak_audience.rb'
|
|
21
|
+
template 'initializer.rb.erb', 'config/initializers/verikloak_audience.rb'
|
|
24
22
|
end
|
|
25
23
|
end
|
|
26
24
|
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Insert the audience middleware after the core Verikloak middleware once it
|
|
4
|
+
# has been loaded into the application.
|
|
5
|
+
if defined?(Rails) && Rails.respond_to?(:application)
|
|
6
|
+
Verikloak::Audience::Railtie.insert_middleware(Rails.application)
|
|
7
|
+
end
|
|
@@ -44,7 +44,7 @@ module Verikloak
|
|
|
44
44
|
# @param required [Array<String>]
|
|
45
45
|
# @return [Boolean]
|
|
46
46
|
def strict_single?(claims, required)
|
|
47
|
-
aud =
|
|
47
|
+
aud = normalized_audiences(claims)
|
|
48
48
|
return false if required.empty?
|
|
49
49
|
|
|
50
50
|
# Must contain all required and have no unexpected extra (order-insensitive)
|
|
@@ -57,7 +57,7 @@ module Verikloak
|
|
|
57
57
|
# @param required [Array<String>]
|
|
58
58
|
# @return [Boolean]
|
|
59
59
|
def allow_account?(claims, required)
|
|
60
|
-
aud =
|
|
60
|
+
aud = normalized_audiences(claims)
|
|
61
61
|
return false if required.empty?
|
|
62
62
|
|
|
63
63
|
# Permit 'account' extra
|
|
@@ -89,13 +89,10 @@ module Verikloak
|
|
|
89
89
|
def suggest(claims, cfg)
|
|
90
90
|
claims = normalize_claims(claims)
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return :strict_single if aud.sort == req.sort
|
|
97
|
-
return :allow_account if (aud - req) == ['account'] && (req - aud).empty?
|
|
98
|
-
return :resource_or_aud if has_roles
|
|
92
|
+
required = cfg.required_aud_list
|
|
93
|
+
return :strict_single if strict_single?(claims, required)
|
|
94
|
+
return :allow_account if allow_account?(claims, required)
|
|
95
|
+
return :resource_or_aud if resource_or_aud?(claims, cfg.resource_client.to_s, required)
|
|
99
96
|
|
|
100
97
|
:strict_single
|
|
101
98
|
end
|
|
@@ -120,6 +117,16 @@ module Verikloak
|
|
|
120
117
|
end
|
|
121
118
|
module_function :normalize_claims
|
|
122
119
|
private_class_method :normalize_claims
|
|
120
|
+
|
|
121
|
+
# Normalize audience claims into a predictable array of strings.
|
|
122
|
+
#
|
|
123
|
+
# @param claims [Hash]
|
|
124
|
+
# @return [Array<String>]
|
|
125
|
+
def normalized_audiences(claims)
|
|
126
|
+
Array(claims['aud']).map(&:to_s)
|
|
127
|
+
end
|
|
128
|
+
module_function :normalized_audiences
|
|
129
|
+
private_class_method :normalized_audiences
|
|
123
130
|
end
|
|
124
131
|
end
|
|
125
132
|
end
|
|
@@ -23,6 +23,7 @@ module Verikloak
|
|
|
23
23
|
# @return [Boolean]
|
|
24
24
|
class Configuration
|
|
25
25
|
DEFAULT_RESOURCE_CLIENT = 'rails-api'
|
|
26
|
+
DEFAULT_ENV_CLAIMS_KEY = 'verikloak.user'
|
|
26
27
|
|
|
27
28
|
attr_accessor :profile, :required_aud, :resource_client,
|
|
28
29
|
:suggest_in_logs
|
|
@@ -35,7 +36,7 @@ module Verikloak
|
|
|
35
36
|
@profile = :strict_single
|
|
36
37
|
@required_aud = []
|
|
37
38
|
@resource_client = DEFAULT_RESOURCE_CLIENT
|
|
38
|
-
self.env_claims_key =
|
|
39
|
+
self.env_claims_key = DEFAULT_ENV_CLAIMS_KEY
|
|
39
40
|
@suggest_in_logs = true
|
|
40
41
|
end
|
|
41
42
|
|
|
@@ -121,23 +122,19 @@ module Verikloak
|
|
|
121
122
|
# @return [void]
|
|
122
123
|
def ensure_resource_client!(audiences)
|
|
123
124
|
client = resource_client.to_s
|
|
125
|
+
error_msg = 'resource_client must match one of required_aud when using :resource_or_aud profile'
|
|
124
126
|
|
|
125
|
-
|
|
127
|
+
if needs_resource_client_inference?(client, audiences)
|
|
128
|
+
raise Verikloak::Audience::ConfigurationError, error_msg unless audiences.one?
|
|
129
|
+
|
|
130
|
+
self.resource_client = audiences.first
|
|
131
|
+
client = resource_client.to_s
|
|
126
132
|
|
|
127
|
-
if needs_inference
|
|
128
|
-
if audiences.one?
|
|
129
|
-
self.resource_client = audiences.first
|
|
130
|
-
client = resource_client.to_s
|
|
131
|
-
else
|
|
132
|
-
raise Verikloak::Audience::ConfigurationError,
|
|
133
|
-
'resource_client must match one of required_aud when using :resource_or_aud profile'
|
|
134
|
-
end
|
|
135
133
|
end
|
|
136
134
|
|
|
137
135
|
return if audiences.include?(client)
|
|
138
136
|
|
|
139
|
-
raise Verikloak::Audience::ConfigurationError,
|
|
140
|
-
'resource_client must match one of required_aud when using :resource_or_aud profile'
|
|
137
|
+
raise Verikloak::Audience::ConfigurationError, error_msg
|
|
141
138
|
end
|
|
142
139
|
|
|
143
140
|
# Decide whether the resource client should be inferred from the
|
|
@@ -57,16 +57,14 @@ module Verikloak
|
|
|
57
57
|
# @return [void]
|
|
58
58
|
def apply_overrides!(opts)
|
|
59
59
|
cfg = @config
|
|
60
|
-
opts.
|
|
60
|
+
opts.each do |key, value|
|
|
61
61
|
writer = "#{key}="
|
|
62
|
-
|
|
62
|
+
unless cfg.respond_to?(writer)
|
|
63
|
+
raise Verikloak::Audience::ConfigurationError,
|
|
64
|
+
"unknown middleware option :#{key}"
|
|
65
|
+
end
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
"unknown middleware option :#{key}"
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
opts.each do |k, v|
|
|
69
|
-
cfg.public_send("#{k}=", v)
|
|
67
|
+
cfg.public_send(writer, value)
|
|
70
68
|
end
|
|
71
69
|
end
|
|
72
70
|
|
|
@@ -90,11 +88,9 @@ module Verikloak
|
|
|
90
88
|
# @return [void]
|
|
91
89
|
def log_warning(env, message)
|
|
92
90
|
logger = env['verikloak.logger'] || env['rack.logger'] || env['action_dispatch.logger']
|
|
93
|
-
if logger.respond_to?(:warn)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
warn(message)
|
|
97
|
-
end
|
|
91
|
+
return logger.warn(message) if logger.respond_to?(:warn)
|
|
92
|
+
|
|
93
|
+
Kernel.warn(message)
|
|
98
94
|
end
|
|
99
95
|
end
|
|
100
96
|
end
|
|
@@ -35,6 +35,7 @@ module Verikloak
|
|
|
35
35
|
|
|
36
36
|
initializer 'verikloak_audience.configuration' do
|
|
37
37
|
config.after_initialize do
|
|
38
|
+
self.class.apply_verikloak_rails_configuration
|
|
38
39
|
next if Verikloak::Audience::Railtie.skip_configuration_validation?
|
|
39
40
|
|
|
40
41
|
Verikloak::Audience.config.validate!
|
|
@@ -45,6 +46,9 @@ module Verikloak
|
|
|
45
46
|
# Verikloak middleware is available and already present. Extracted for
|
|
46
47
|
# testability without requiring a full Rails boot process.
|
|
47
48
|
#
|
|
49
|
+
# Insert the audience middleware after the base Verikloak middleware when
|
|
50
|
+
# both are available on the stack.
|
|
51
|
+
#
|
|
48
52
|
# @param app [#middleware] An object exposing a Rack middleware stack via `#middleware`.
|
|
49
53
|
# @return [void]
|
|
50
54
|
def self.insert_middleware(app)
|
|
@@ -53,6 +57,8 @@ module Verikloak
|
|
|
53
57
|
middleware_stack = app.middleware
|
|
54
58
|
return unless middleware_stack.respond_to?(:include?)
|
|
55
59
|
|
|
60
|
+
return if middleware_stack.include?(::Verikloak::Audience::Middleware)
|
|
61
|
+
|
|
56
62
|
unless middleware_stack.include?(::Verikloak::Middleware)
|
|
57
63
|
warn_missing_core_middleware
|
|
58
64
|
return
|
|
@@ -85,7 +91,7 @@ module Verikloak
|
|
|
85
91
|
#
|
|
86
92
|
# @return [void]
|
|
87
93
|
def self.warn_missing_core_middleware
|
|
88
|
-
logger =
|
|
94
|
+
logger = (::Rails.logger if defined?(::Rails) && ::Rails.respond_to?(:logger))
|
|
89
95
|
|
|
90
96
|
if logger
|
|
91
97
|
logger.warn(WARNING_MESSAGE)
|
|
@@ -94,20 +100,6 @@ module Verikloak
|
|
|
94
100
|
end
|
|
95
101
|
end
|
|
96
102
|
|
|
97
|
-
# Retrieves the Rails application logger if available.
|
|
98
|
-
#
|
|
99
|
-
# This method safely attempts to access the Rails logger, returning nil
|
|
100
|
-
# if Rails is not defined, doesn't respond to the logger method, or if
|
|
101
|
-
# the logger itself is nil.
|
|
102
|
-
#
|
|
103
|
-
# @return [Logger, nil] the Rails logger instance, or nil if unavailable
|
|
104
|
-
def self.rails_logger
|
|
105
|
-
return unless defined?(::Rails)
|
|
106
|
-
return unless ::Rails.respond_to?(:logger)
|
|
107
|
-
|
|
108
|
-
::Rails.logger
|
|
109
|
-
end
|
|
110
|
-
|
|
111
103
|
# Rails short commands (`g`, `d`) are stripped from ARGV fairly early in
|
|
112
104
|
# the boot process. Treat `verikloak:*:install` generators as safe so they
|
|
113
105
|
# can run before configuration files exist.
|
|
@@ -119,25 +111,32 @@ module Verikloak
|
|
|
119
111
|
#
|
|
120
112
|
# @return [Boolean]
|
|
121
113
|
def self.skip_configuration_validation?
|
|
122
|
-
|
|
123
|
-
return false
|
|
114
|
+
tokens = first_cli_tokens
|
|
115
|
+
return false if tokens.empty?
|
|
116
|
+
|
|
117
|
+
command = tokens.first
|
|
118
|
+
return true if COMMANDS_SKIPPING_VALIDATION.include?(command)
|
|
124
119
|
|
|
125
|
-
|
|
120
|
+
tokens.any? { |token| verikloak_install_generator?(token) }
|
|
126
121
|
end
|
|
127
122
|
|
|
128
|
-
# Capture the first non-option
|
|
129
|
-
# ignoring wrapper tokens such as "rails".
|
|
123
|
+
# Capture the first non-option arguments passed to the Rails CLI,
|
|
124
|
+
# ignoring wrapper tokens such as "rails". Only the first two tokens are
|
|
125
|
+
# relevant for generator detection, so we keep the return list short.
|
|
130
126
|
#
|
|
131
|
-
# @return [String
|
|
132
|
-
def self.
|
|
127
|
+
# @return [Array<String>] ordered CLI tokens that may signal a generator
|
|
128
|
+
def self.first_cli_tokens
|
|
129
|
+
tokens = []
|
|
130
|
+
|
|
133
131
|
ARGV.each do |arg|
|
|
134
132
|
next if arg.start_with?('-')
|
|
135
133
|
next if arg == 'rails'
|
|
136
134
|
|
|
137
|
-
|
|
135
|
+
tokens << arg
|
|
136
|
+
break if tokens.size >= 2
|
|
138
137
|
end
|
|
139
138
|
|
|
140
|
-
|
|
139
|
+
tokens
|
|
141
140
|
end
|
|
142
141
|
|
|
143
142
|
# Detect whether the provided CLI token refers to a Verikloak install
|
|
@@ -150,6 +149,118 @@ module Verikloak
|
|
|
150
149
|
|
|
151
150
|
command.start_with?('verikloak:') && command.end_with?(':install')
|
|
152
151
|
end
|
|
152
|
+
|
|
153
|
+
class << self
|
|
154
|
+
# Synchronize configuration with verikloak-rails when it is present.
|
|
155
|
+
# Aligns env_claims_key, required_aud, and resource_client defaults so
|
|
156
|
+
# that both gems operate on the same Rack env payload and audience list.
|
|
157
|
+
#
|
|
158
|
+
# @return [void]
|
|
159
|
+
def apply_verikloak_rails_configuration
|
|
160
|
+
rails_config = verikloak_rails_config
|
|
161
|
+
return unless rails_config
|
|
162
|
+
|
|
163
|
+
Verikloak::Audience.configure do |cfg|
|
|
164
|
+
sync_env_claims_key(cfg, rails_config)
|
|
165
|
+
sync_required_aud(cfg, rails_config)
|
|
166
|
+
sync_resource_client(cfg, rails_config)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
# Resolve the verikloak-rails configuration object if the gem is loaded.
|
|
173
|
+
#
|
|
174
|
+
# @return [Verikloak::Rails::Configuration, nil]
|
|
175
|
+
def verikloak_rails_config
|
|
176
|
+
return unless defined?(::Verikloak::Rails)
|
|
177
|
+
return unless ::Verikloak::Rails.respond_to?(:config)
|
|
178
|
+
|
|
179
|
+
::Verikloak::Rails.config
|
|
180
|
+
rescue StandardError
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Align the environment claims key with the one configured in verikloak-rails.
|
|
185
|
+
#
|
|
186
|
+
# @param cfg [Verikloak::Audience::Configuration]
|
|
187
|
+
# @param rails_config [Verikloak::Rails::Configuration]
|
|
188
|
+
# @return [void]
|
|
189
|
+
def sync_env_claims_key(cfg, rails_config)
|
|
190
|
+
return unless rails_config.respond_to?(:user_env_key)
|
|
191
|
+
|
|
192
|
+
user_key = rails_config.user_env_key
|
|
193
|
+
return if blank?(user_key)
|
|
194
|
+
|
|
195
|
+
current = cfg.env_claims_key
|
|
196
|
+
return unless current.nil? || current == Verikloak::Audience::Configuration::DEFAULT_ENV_CLAIMS_KEY
|
|
197
|
+
|
|
198
|
+
cfg.env_claims_key = user_key
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Populate required audiences from the verikloak-rails configuration when absent.
|
|
202
|
+
#
|
|
203
|
+
# @param cfg [Verikloak::Audience::Configuration]
|
|
204
|
+
# @param rails_config [Verikloak::Rails::Configuration]
|
|
205
|
+
# @return [void]
|
|
206
|
+
def sync_required_aud(cfg, rails_config)
|
|
207
|
+
return unless cfg_required_aud_blank?(cfg)
|
|
208
|
+
return unless rails_config.respond_to?(:audience)
|
|
209
|
+
|
|
210
|
+
audiences = normalized_audiences(rails_config.audience)
|
|
211
|
+
return if audiences.empty?
|
|
212
|
+
|
|
213
|
+
cfg.required_aud = audiences.size == 1 ? audiences.first : audiences
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Infer the resource client based on the configured audience when possible.
|
|
217
|
+
#
|
|
218
|
+
# @param cfg [Verikloak::Audience::Configuration]
|
|
219
|
+
# @param rails_config [Verikloak::Rails::Configuration]
|
|
220
|
+
# @return [void]
|
|
221
|
+
def sync_resource_client(cfg, rails_config)
|
|
222
|
+
return unless rails_config.respond_to?(:audience)
|
|
223
|
+
|
|
224
|
+
audiences = normalized_audiences(rails_config.audience)
|
|
225
|
+
return unless audiences.size == 1
|
|
226
|
+
|
|
227
|
+
current_client = cfg.resource_client
|
|
228
|
+
unless blank?(current_client) || current_client == Verikloak::Audience::Configuration::DEFAULT_RESOURCE_CLIENT
|
|
229
|
+
return
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
cfg.resource_client = audiences.first
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Determine whether the audience configuration is effectively empty.
|
|
236
|
+
#
|
|
237
|
+
# @param cfg [Verikloak::Audience::Configuration]
|
|
238
|
+
# @return [Boolean]
|
|
239
|
+
def cfg_required_aud_blank?(cfg)
|
|
240
|
+
value_blank?(cfg.required_aud)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Generic blank? helper that tolerates nil, empty, or blank-ish values.
|
|
244
|
+
#
|
|
245
|
+
# @param value [Object]
|
|
246
|
+
# @return [Boolean]
|
|
247
|
+
def value_blank?(value)
|
|
248
|
+
return true if value.nil?
|
|
249
|
+
return true if value.respond_to?(:empty?) && value.empty?
|
|
250
|
+
|
|
251
|
+
value.to_s.empty?
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
alias blank? value_blank?
|
|
255
|
+
|
|
256
|
+
# Coerce the given source into an array of non-empty string audiences.
|
|
257
|
+
#
|
|
258
|
+
# @param source [Object]
|
|
259
|
+
# @return [Array<String>]
|
|
260
|
+
def normalized_audiences(source)
|
|
261
|
+
Array(source).compact.map(&:to_s).reject(&:empty?)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
153
264
|
end
|
|
154
265
|
end
|
|
155
266
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: verikloak-audience
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- taiyaky
|
|
@@ -60,6 +60,7 @@ files:
|
|
|
60
60
|
- LICENSE
|
|
61
61
|
- README.md
|
|
62
62
|
- lib/generators/verikloak/audience/install/install_generator.rb
|
|
63
|
+
- lib/generators/verikloak/audience/install/templates/initializer.rb.erb
|
|
63
64
|
- lib/verikloak-audience.rb
|
|
64
65
|
- lib/verikloak/audience.rb
|
|
65
66
|
- lib/verikloak/audience/checker.rb
|
|
@@ -75,7 +76,7 @@ metadata:
|
|
|
75
76
|
source_code_uri: https://github.com/taiyaky/verikloak-audience
|
|
76
77
|
changelog_uri: https://github.com/taiyaky/verikloak-audience/blob/main/CHANGELOG.md
|
|
77
78
|
bug_tracker_uri: https://github.com/taiyaky/verikloak-audience/issues
|
|
78
|
-
documentation_uri: https://rubydoc.info/gems/verikloak-audience/0.2.
|
|
79
|
+
documentation_uri: https://rubydoc.info/gems/verikloak-audience/0.2.7
|
|
79
80
|
rubygems_mfa_required: 'true'
|
|
80
81
|
rdoc_options: []
|
|
81
82
|
require_paths:
|