verikloak-bff 0.2.2 → 0.2.4
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 +18 -0
- data/README.md +6 -2
- data/lib/generators/verikloak/bff/install/install_generator.rb +48 -0
- data/lib/generators/verikloak/bff/install/templates/initializer.rb.erb +11 -0
- data/lib/verikloak/bff/consistency_checks.rb +2 -7
- data/lib/verikloak/bff/header_guard.rb +56 -16
- data/lib/verikloak/bff/jwt_utils.rb +33 -0
- data/lib/verikloak/bff/proxy_trust.rb +31 -27
- data/lib/verikloak/bff/rails.rb +78 -47
- data/lib/verikloak/bff/railtie.rb +13 -15
- data/lib/verikloak/bff/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 459eed20d30ee0052dda42e6e1b18c4ac4f58aa897ba24a0090d19931a28690e
|
|
4
|
+
data.tar.gz: 20d3f106c2439d3f63b683a79f900d9c072893ec730d9d294e610817b560777e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 47838af8ec9b12a4570e967c188ffcaf7a4ce9556aa06409d6fce1906bc4fd0b16568342baacec85a46883f9161fe015459996453e724e37c7c6a6d7e339e1c8
|
|
7
|
+
data.tar.gz: b12bcca4f52ba05a8aba8fe145c1664743e70127120642f7bf334aa8db1499301badd988d5a92040d359fc2e61f94d1bbf79699f972fc7f05c12867702a10e72
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.2.4] - 2025-09-27
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Simplify BFF install generator by inlining configuration lookups and removing unnecessary helper methods.
|
|
14
|
+
- Streamline generated initializer to use `Rails.configuration.middleware` and `Rails.logger` directly.
|
|
15
|
+
- Extract JWT decoding logic into shared `JwtUtils` module to eliminate duplication between `HeaderGuard` and `ConsistencyChecks`.
|
|
16
|
+
- Refactor `HeaderGuard#call` into clear pipeline stages with improved documentation.
|
|
17
|
+
- Enhance middleware stack detection to handle wrapped entries, string names, and complex objects.
|
|
18
|
+
- Remove duplicate proxy trust logic in `ProxyTrust` module by unifying `from_trusted_proxy?` and `trusted?` methods.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Resolve RuboCop style violations including useless constant scoping and identical conditional branches.
|
|
22
|
+
|
|
23
|
+
## [0.2.3] - 2025-09-23
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Stop inserting `Verikloak::BFF::HeaderGuard` automatically via the Railtie and provide a `rails g verikloak:bff:install` generator that drops an initializer to opt in when the core middleware is ready.
|
|
27
|
+
|
|
10
28
|
## [0.2.2] - 2025-09-23
|
|
11
29
|
|
|
12
30
|
### Changed
|
data/README.md
CHANGED
|
@@ -31,12 +31,16 @@ use Verikloak::BFF::HeaderGuard, trusted_proxies: ['127.0.0.1', '10.0.0.0/8']
|
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
### Rails Applications
|
|
34
|
-
|
|
34
|
+
Add the gem to your Gemfile and run the install generator to drop an initializer that wires the middleware into Rails:
|
|
35
35
|
```ruby
|
|
36
36
|
gem 'verikloak-bff'
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
```sh
|
|
40
|
+
bin/rails g verikloak:bff:install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The generated initializer inserts `Verikloak::BFF::HeaderGuard` after the core `Verikloak::Middleware` during boot. If the core middleware is not present (for example, when verikloak-rails has not been fully configured yet), the initializer logs a warning and allows Rails to boot normally.
|
|
40
44
|
|
|
41
45
|
For detailed configuration, proxy setup examples, and troubleshooting, see [docs/rails.md](docs/rails.md).
|
|
42
46
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators/base'
|
|
4
|
+
|
|
5
|
+
module Verikloak
|
|
6
|
+
module BFF
|
|
7
|
+
# Namespace for Rails generators related to Verikloak BFF
|
|
8
|
+
module Generators
|
|
9
|
+
# Rails generator for installing Verikloak BFF middleware integration.
|
|
10
|
+
#
|
|
11
|
+
# This generator creates a Rails initializer that safely inserts the
|
|
12
|
+
# Verikloak::BFF::HeaderGuard middleware into the Rails middleware stack.
|
|
13
|
+
# It replaces automatic middleware insertion to avoid boot failures when
|
|
14
|
+
# core Verikloak middleware is not yet configured.
|
|
15
|
+
#
|
|
16
|
+
# @example Basic usage
|
|
17
|
+
# rails g verikloak:bff:install
|
|
18
|
+
#
|
|
19
|
+
# @example Custom initializer path
|
|
20
|
+
# rails g verikloak:bff:install --initializer=config/initializers/custom_bff.rb
|
|
21
|
+
#
|
|
22
|
+
# @see Verikloak::BFF::Rails::Middleware
|
|
23
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
24
|
+
source_root File.expand_path('templates', __dir__)
|
|
25
|
+
|
|
26
|
+
# Configuration option for specifying the initializer file path.
|
|
27
|
+
#
|
|
28
|
+
# @option options [String] :initializer ('config/initializers/verikloak_bff.rb')
|
|
29
|
+
# The path where the initializer file will be created
|
|
30
|
+
class_option :initializer, type: :string,
|
|
31
|
+
default: 'config/initializers/verikloak_bff.rb',
|
|
32
|
+
desc: 'Path for the generated initializer'
|
|
33
|
+
|
|
34
|
+
# Creates the Rails initializer file from template.
|
|
35
|
+
#
|
|
36
|
+
# Generates a Rails initializer that safely inserts the HeaderGuard
|
|
37
|
+
# middleware into the middleware stack with proper error handling.
|
|
38
|
+
# The initializer uses Verikloak::BFF::Rails::Middleware.insert_after_core
|
|
39
|
+
# to ensure graceful handling when core middleware is missing.
|
|
40
|
+
#
|
|
41
|
+
# @return [void]
|
|
42
|
+
def create_initializer
|
|
43
|
+
template 'initializer.rb.erb', options.fetch(:initializer)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Configure verikloak-bff to insert its HeaderGuard middleware only after the
|
|
4
|
+
# Verikloak core middleware is present. This avoids boot errors when the core
|
|
5
|
+
# gem has not yet been installed.
|
|
6
|
+
Rails.application.config.after_initialize do
|
|
7
|
+
Verikloak::BFF::Rails::Middleware.insert_after_core(
|
|
8
|
+
Rails.application.config.middleware,
|
|
9
|
+
logger: Rails.logger
|
|
10
|
+
)
|
|
11
|
+
end
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# @see .enforce!
|
|
7
7
|
|
|
8
8
|
require 'json'
|
|
9
|
-
require 'jwt' # used only to parse segments safely without verify
|
|
10
9
|
require 'verikloak/bff/constants'
|
|
10
|
+
require 'verikloak/bff/jwt_utils'
|
|
11
11
|
|
|
12
12
|
module Verikloak
|
|
13
13
|
module BFF
|
|
@@ -21,12 +21,7 @@ module Verikloak
|
|
|
21
21
|
# @param token [String, nil]
|
|
22
22
|
# @return [Hash] claims or empty hash on error
|
|
23
23
|
def decode_claims(token)
|
|
24
|
-
|
|
25
|
-
return {} if token.bytesize > Constants::MAX_TOKEN_BYTES
|
|
26
|
-
|
|
27
|
-
JWT.decode(token, nil, false).first
|
|
28
|
-
rescue StandardError
|
|
29
|
-
{}
|
|
24
|
+
Verikloak::BFF::JwtUtils.decode_claims(token)
|
|
30
25
|
end
|
|
31
26
|
|
|
32
27
|
# mapping: { email: :email, user: :sub, groups: :realm_roles }
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
require 'rack'
|
|
14
14
|
require 'rack/utils'
|
|
15
15
|
require 'json'
|
|
16
|
-
require 'jwt'
|
|
17
16
|
require 'digest'
|
|
18
17
|
require 'verikloak/header_sources'
|
|
19
18
|
require 'verikloak/bff/configuration'
|
|
@@ -22,6 +21,7 @@ require 'verikloak/bff/proxy_trust'
|
|
|
22
21
|
require 'verikloak/bff/forwarded_token'
|
|
23
22
|
require 'verikloak/bff/consistency_checks'
|
|
24
23
|
require 'verikloak/bff/constants'
|
|
24
|
+
require 'verikloak/bff/jwt_utils'
|
|
25
25
|
|
|
26
26
|
module Verikloak
|
|
27
27
|
module BFF
|
|
@@ -58,11 +58,7 @@ module Verikloak
|
|
|
58
58
|
# @param token [String, nil]
|
|
59
59
|
# @return [Array<Hash>] payload and header hashes
|
|
60
60
|
def decode_unverified(token)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
JWT.decode(token, nil, false)
|
|
64
|
-
rescue StandardError
|
|
65
|
-
[{}, {}]
|
|
61
|
+
Verikloak::BFF::JwtUtils.decode_unverified(token)
|
|
66
62
|
end
|
|
67
63
|
|
|
68
64
|
# Remove unsafe characters from a structured logging payload.
|
|
@@ -104,6 +100,8 @@ module Verikloak
|
|
|
104
100
|
|
|
105
101
|
# Rack middleware that enforces BFF boundary and header/claims consistency.
|
|
106
102
|
class HeaderGuard
|
|
103
|
+
RequestTokens = Struct.new(:auth, :forwarded, :chosen)
|
|
104
|
+
|
|
107
105
|
# Accept both Rack 2 and Rack 3 builder call styles:
|
|
108
106
|
# - new(app, key: val)
|
|
109
107
|
# - new(app, { key: val })
|
|
@@ -125,22 +123,29 @@ module Verikloak
|
|
|
125
123
|
raise ArgumentError, 'trusted_proxies must be configured'
|
|
126
124
|
end
|
|
127
125
|
|
|
128
|
-
# Process a Rack request.
|
|
126
|
+
# Process a Rack request through the BFF header guard pipeline.
|
|
127
|
+
#
|
|
128
|
+
# Pipeline stages:
|
|
129
|
+
# 1. Proxy trust validation
|
|
130
|
+
# 2. Token extraction and state building
|
|
131
|
+
# 3. Policy enforcement (forwarded token requirements, consistency checks)
|
|
132
|
+
# 4. Request finalization (Authorization header normalization)
|
|
129
133
|
#
|
|
130
134
|
# @param env [Hash]
|
|
131
135
|
# @return [Array(Integer, Hash, Array<#to_s>)] Rack response triple
|
|
132
136
|
def call(env)
|
|
137
|
+
# Stage 1: Validate request comes from trusted proxy
|
|
133
138
|
ensure_trusted_proxy!(env)
|
|
134
|
-
auth_token, fwd_token = ForwardedToken.extract(env, @config.forwarded_header_name)
|
|
135
|
-
ensure_forwarded_if_required!(fwd_token)
|
|
136
|
-
chosen = choose_token(auth_token, fwd_token)
|
|
137
|
-
chosen = seed_authorization_if_needed(env, chosen)
|
|
138
139
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
# Stage 2: Extract and validate tokens, build request state
|
|
141
|
+
tokens = build_token_state(env)
|
|
142
|
+
|
|
143
|
+
# Stage 3: Enforce configured policies (forwarded requirements, consistency)
|
|
144
|
+
enforce_token_policies!(env, tokens)
|
|
145
|
+
|
|
146
|
+
# Stage 4: Finalize request with normalized Authorization header
|
|
147
|
+
finalize_request!(env, tokens)
|
|
148
|
+
|
|
144
149
|
@app.call(env)
|
|
145
150
|
rescue Verikloak::BFF::Error => e
|
|
146
151
|
respond_with_error(env, e)
|
|
@@ -148,6 +153,41 @@ module Verikloak
|
|
|
148
153
|
|
|
149
154
|
private
|
|
150
155
|
|
|
156
|
+
# Build token state by extracting, validating, and selecting the active token.
|
|
157
|
+
#
|
|
158
|
+
# @param env [Hash]
|
|
159
|
+
# @return [RequestTokens]
|
|
160
|
+
def build_token_state(env)
|
|
161
|
+
auth_token, fwd_token = ForwardedToken.extract(env, @config.forwarded_header_name)
|
|
162
|
+
ensure_forwarded_if_required!(fwd_token)
|
|
163
|
+
|
|
164
|
+
chosen = choose_token(auth_token, fwd_token)
|
|
165
|
+
chosen = seed_authorization_if_needed(env, chosen)
|
|
166
|
+
|
|
167
|
+
RequestTokens.new(auth_token, fwd_token, chosen)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Apply header and claim consistency policies for the current request.
|
|
171
|
+
#
|
|
172
|
+
# @param env [Hash]
|
|
173
|
+
# @param tokens [RequestTokens]
|
|
174
|
+
# @return [void]
|
|
175
|
+
def enforce_token_policies!(env, tokens)
|
|
176
|
+
enforce_header_consistency!(env, tokens.auth, tokens.forwarded)
|
|
177
|
+
enforce_claims_consistency!(env, tokens.chosen)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Mutate the Rack env with normalized headers and logging hints.
|
|
181
|
+
#
|
|
182
|
+
# @param env [Hash]
|
|
183
|
+
# @param tokens [RequestTokens]
|
|
184
|
+
# @return [void]
|
|
185
|
+
def finalize_request!(env, tokens)
|
|
186
|
+
ForwardedToken.strip_suspicious!(env, @config.auth_request_headers) if @config.strip_suspicious_headers
|
|
187
|
+
normalize_authorization!(env, tokens.chosen, tokens.auth, tokens.forwarded)
|
|
188
|
+
expose_env_hints(env, tokens.chosen)
|
|
189
|
+
end
|
|
190
|
+
|
|
151
191
|
# Apply per-instance configuration overrides.
|
|
152
192
|
#
|
|
153
193
|
# @param opts [Hash]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'jwt'
|
|
4
|
+
require 'verikloak/bff/constants'
|
|
5
|
+
|
|
6
|
+
module Verikloak
|
|
7
|
+
module BFF
|
|
8
|
+
# Lightweight helpers around JWT decoding without verification.
|
|
9
|
+
module JwtUtils
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# Decode JWT header and payload without verification, guarding against oversized input.
|
|
13
|
+
#
|
|
14
|
+
# @param token [String, nil]
|
|
15
|
+
# @return [Array<Hash>] [payload, header]
|
|
16
|
+
def decode_unverified(token)
|
|
17
|
+
return [{}, {}] if token.nil? || token.bytesize > Constants::MAX_TOKEN_BYTES
|
|
18
|
+
|
|
19
|
+
JWT.decode(token, nil, false)
|
|
20
|
+
rescue StandardError
|
|
21
|
+
[{}, {}]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Return the decoded JWT payload without verification.
|
|
25
|
+
#
|
|
26
|
+
# @param token [String, nil]
|
|
27
|
+
# @return [Hash]
|
|
28
|
+
def decode_claims(token)
|
|
29
|
+
decode_unverified(token).first
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -23,17 +23,8 @@ module Verikloak
|
|
|
23
23
|
# @example CIDR + Regex allowlist
|
|
24
24
|
# ProxyTrust.trusted?(env, ["10.0.0.0/8", /^192\.168\./], :rightmost)
|
|
25
25
|
def trusted?(env, trusted, strategy = :rightmost)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# Rails-aligned: prefer REMOTE_ADDR; fallback to nearest XFF entry
|
|
29
|
-
remote = (env['REMOTE_ADDR'] || '').to_s.strip
|
|
30
|
-
remote = extract_peer_ip(env, strategy) if remote.empty?
|
|
31
|
-
return false unless remote
|
|
32
|
-
|
|
33
|
-
remote_ip = ip_or_nil(remote)
|
|
34
|
-
trusted.any? { |rule| rule_trusts?(rule, remote, remote_ip, env) }
|
|
35
|
-
rescue StandardError
|
|
36
|
-
false
|
|
26
|
+
remote = resolve_peer(env, :remote_then_xff, strategy)
|
|
27
|
+
trusted_remote?(remote, trusted, env)
|
|
37
28
|
end
|
|
38
29
|
|
|
39
30
|
# Select the peer IP from X-Forwarded-For according to strategy or fall back to REMOTE_ADDR.
|
|
@@ -59,15 +50,7 @@ module Verikloak
|
|
|
59
50
|
# @param strategy [Symbol] :rightmost or :leftmost
|
|
60
51
|
# @return [String, nil]
|
|
61
52
|
def selected_peer(env, preference, strategy)
|
|
62
|
-
|
|
63
|
-
when :remote_then_xff
|
|
64
|
-
ip = (env['REMOTE_ADDR'] || '').to_s.strip
|
|
65
|
-
return ip unless ip.nil? || ip.empty?
|
|
66
|
-
|
|
67
|
-
extract_peer_ip(env, :rightmost) # nearest by default
|
|
68
|
-
else
|
|
69
|
-
extract_peer_ip(env, strategy)
|
|
70
|
-
end
|
|
53
|
+
resolve_peer(env, preference, strategy)
|
|
71
54
|
end
|
|
72
55
|
|
|
73
56
|
# Parse string to IPAddr or nil on failure.
|
|
@@ -112,16 +95,37 @@ module Verikloak
|
|
|
112
95
|
# @param trusted [Array<String, Regexp, Proc>, nil]
|
|
113
96
|
# @return [Boolean]
|
|
114
97
|
def self.from_trusted_proxy?(env, trusted)
|
|
115
|
-
|
|
98
|
+
trusted?(env, trusted, :rightmost)
|
|
99
|
+
end
|
|
116
100
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
101
|
+
# Resolve the peer value based on preference and strategy.
|
|
102
|
+
#
|
|
103
|
+
# @param env [Hash]
|
|
104
|
+
# @param preference [Symbol]
|
|
105
|
+
# @param strategy [Symbol]
|
|
106
|
+
# @return [String, nil]
|
|
107
|
+
def resolve_peer(env, preference, strategy)
|
|
108
|
+
case preference.to_s.to_sym
|
|
109
|
+
when :remote_then_xff
|
|
110
|
+
remote = (env['REMOTE_ADDR'] || '').to_s.strip
|
|
111
|
+
return remote unless remote.empty?
|
|
112
|
+
# Fall back to X-Forwarded-For when REMOTE_ADDR is empty
|
|
113
|
+
end
|
|
114
|
+
extract_peer_ip(env, strategy)
|
|
115
|
+
end
|
|
120
116
|
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
# Determine whether a remote peer appears in the trusted list.
|
|
118
|
+
#
|
|
119
|
+
# @param remote [String, nil]
|
|
120
|
+
# @param trusted [Array<String, Regexp, Proc>, nil]
|
|
121
|
+
# @param env [Hash]
|
|
122
|
+
# @return [Boolean]
|
|
123
|
+
def trusted_remote?(remote, trusted, env)
|
|
124
|
+
return false if trusted.nil? || trusted.empty?
|
|
125
|
+
return false unless remote
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
remote_ip = ip_or_nil(remote)
|
|
128
|
+
trusted.any? { |rule| rule_trusts?(rule, remote, remote_ip, env) }
|
|
125
129
|
rescue StandardError
|
|
126
130
|
false
|
|
127
131
|
end
|
data/lib/verikloak/bff/rails.rb
CHANGED
|
@@ -4,14 +4,20 @@ module Verikloak
|
|
|
4
4
|
module BFF
|
|
5
5
|
# Rails-specific functionality for Verikloak BFF
|
|
6
6
|
module Rails
|
|
7
|
-
# Middleware management utilities for Rails applications
|
|
7
|
+
# Middleware management utilities for Rails applications.
|
|
8
8
|
#
|
|
9
|
-
# This module
|
|
10
|
-
#
|
|
11
|
-
# the core
|
|
9
|
+
# This module focuses on inserting the HeaderGuard middleware right after
|
|
10
|
+
# the core Verikloak middleware while gracefully handling stacks that do
|
|
11
|
+
# not contain the core component.
|
|
12
12
|
module Middleware
|
|
13
13
|
module_function
|
|
14
14
|
|
|
15
|
+
CORE_NAME = 'Verikloak::Middleware'
|
|
16
|
+
HEADER_GUARD_NAME = 'Verikloak::BFF::HeaderGuard'
|
|
17
|
+
SKIP_MESSAGE = <<~MSG.chomp.freeze
|
|
18
|
+
[verikloak-bff] Skipping Verikloak::BFF::HeaderGuard insertion because Verikloak::Middleware is not present. Configure verikloak-rails discovery settings and restart once core verification is enabled.
|
|
19
|
+
MSG
|
|
20
|
+
|
|
15
21
|
# Inserts Verikloak::BFF::HeaderGuard middleware after Verikloak::Middleware
|
|
16
22
|
#
|
|
17
23
|
# Attempts to insert the HeaderGuard middleware into the Rails middleware stack
|
|
@@ -31,12 +37,15 @@ module Verikloak
|
|
|
31
37
|
def insert_after_core(stack, logger: nil)
|
|
32
38
|
return false unless auto_insert_enabled?
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
core = core_middleware
|
|
41
|
+
header_guard = header_guard_middleware
|
|
42
|
+
|
|
43
|
+
unless stack && core && header_guard && core_present?(stack, core)
|
|
35
44
|
log_skip(logger)
|
|
36
45
|
return false
|
|
37
46
|
end
|
|
38
47
|
|
|
39
|
-
stack.insert_after(
|
|
48
|
+
stack.insert_after(core, header_guard)
|
|
40
49
|
true
|
|
41
50
|
rescue RuntimeError => e
|
|
42
51
|
raise unless missing_core?(e)
|
|
@@ -54,11 +63,9 @@ module Verikloak
|
|
|
54
63
|
#
|
|
55
64
|
# @return [Boolean]
|
|
56
65
|
def auto_insert_enabled?
|
|
57
|
-
|
|
58
|
-
return true unless ::Verikloak.respond_to?(:config)
|
|
59
|
-
|
|
60
|
-
config = ::Verikloak.config
|
|
66
|
+
config = core_config
|
|
61
67
|
return true unless config
|
|
68
|
+
|
|
62
69
|
return config.auto_insert_bff_header_guard if config.respond_to?(:auto_insert_bff_header_guard)
|
|
63
70
|
|
|
64
71
|
true
|
|
@@ -66,56 +73,60 @@ module Verikloak
|
|
|
66
73
|
true
|
|
67
74
|
end
|
|
68
75
|
|
|
76
|
+
# Returns the Verikloak configuration object when available.
|
|
77
|
+
#
|
|
78
|
+
# @return [Object, nil]
|
|
79
|
+
def core_config
|
|
80
|
+
return nil unless defined?(::Verikloak)
|
|
81
|
+
return nil unless ::Verikloak.respond_to?(:config)
|
|
82
|
+
|
|
83
|
+
::Verikloak.config
|
|
84
|
+
rescue StandardError
|
|
85
|
+
nil
|
|
86
|
+
end
|
|
87
|
+
|
|
69
88
|
# Detect whether the Verikloak core middleware is already present in the stack.
|
|
70
89
|
#
|
|
71
90
|
# @param stack [#include?, #each, nil]
|
|
72
91
|
# @return [Boolean]
|
|
73
|
-
def core_present?(stack)
|
|
92
|
+
def core_present?(stack, core = core_middleware)
|
|
74
93
|
return false unless stack
|
|
75
94
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
# Fall back to manual enumeration when include? is unsupported for this stack
|
|
81
|
-
end
|
|
95
|
+
begin
|
|
96
|
+
return true if core && stack.respond_to?(:include?) && stack.include?(core)
|
|
97
|
+
rescue StandardError
|
|
98
|
+
# Fall back to manual enumeration when include? is unsupported for this stack.
|
|
82
99
|
end
|
|
83
100
|
|
|
84
101
|
return false unless stack.respond_to?(:each)
|
|
85
102
|
|
|
86
|
-
stack.each do |
|
|
87
|
-
|
|
103
|
+
stack.each do |entry|
|
|
104
|
+
candidate = unwrap_middleware(entry)
|
|
105
|
+
return true if core && candidate == core
|
|
106
|
+
return true if middleware_name(candidate) == CORE_NAME
|
|
88
107
|
end
|
|
89
108
|
|
|
90
109
|
false
|
|
91
110
|
end
|
|
92
111
|
|
|
93
|
-
#
|
|
112
|
+
# Normalize raw stack entries to a comparable object.
|
|
94
113
|
#
|
|
95
|
-
# @param
|
|
96
|
-
# @return [
|
|
97
|
-
def
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
klass = extract_middleware_class(candidate)
|
|
101
|
-
|
|
102
|
-
klass == ::Verikloak::Middleware ||
|
|
103
|
-
(klass.is_a?(String) && klass == 'Verikloak::Middleware') ||
|
|
104
|
-
(klass.respond_to?(:name) && klass.name == 'Verikloak::Middleware')
|
|
114
|
+
# @param entry [Object]
|
|
115
|
+
# @return [Object]
|
|
116
|
+
def unwrap_middleware(entry)
|
|
117
|
+
entry = entry.first if entry.is_a?(Array)
|
|
118
|
+
entry.respond_to?(:klass) ? entry.klass : entry
|
|
105
119
|
end
|
|
106
120
|
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
# @param
|
|
110
|
-
# @return [
|
|
111
|
-
def
|
|
112
|
-
if
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
else
|
|
117
|
-
candidate
|
|
118
|
-
end
|
|
121
|
+
# Resolve a human-readable middleware name if possible.
|
|
122
|
+
#
|
|
123
|
+
# @param entry [Object]
|
|
124
|
+
# @return [String, nil]
|
|
125
|
+
def middleware_name(entry)
|
|
126
|
+
return entry if entry.is_a?(String)
|
|
127
|
+
return entry.to_s if entry.is_a?(Symbol)
|
|
128
|
+
|
|
129
|
+
entry.respond_to?(:name) ? entry.name : nil
|
|
119
130
|
end
|
|
120
131
|
|
|
121
132
|
# Checks if the error indicates missing core Verikloak middleware
|
|
@@ -134,7 +145,7 @@ module Verikloak
|
|
|
134
145
|
# end
|
|
135
146
|
def missing_core?(error)
|
|
136
147
|
error.message.include?('No such middleware') &&
|
|
137
|
-
error.message.include?(
|
|
148
|
+
error.message.include?(CORE_NAME)
|
|
138
149
|
end
|
|
139
150
|
|
|
140
151
|
# Logs a warning message about skipping middleware insertion
|
|
@@ -151,11 +162,31 @@ module Verikloak
|
|
|
151
162
|
# @example Logging without logger (uses warn)
|
|
152
163
|
# log_skip(nil)
|
|
153
164
|
def log_skip(logger)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
MSG
|
|
165
|
+
logger ? logger.warn(SKIP_MESSAGE) : warn(SKIP_MESSAGE)
|
|
166
|
+
end
|
|
157
167
|
|
|
158
|
-
|
|
168
|
+
# Safely resolves the core middleware constant when available.
|
|
169
|
+
#
|
|
170
|
+
# @return [Class, nil]
|
|
171
|
+
def core_middleware
|
|
172
|
+
safe_const_get(CORE_NAME)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Safely resolves the HeaderGuard middleware constant when available.
|
|
176
|
+
#
|
|
177
|
+
# @return [Class, nil]
|
|
178
|
+
def header_guard_middleware
|
|
179
|
+
safe_const_get(HEADER_GUARD_NAME)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Attempts to constantize the provided class name, returning nil when undefined.
|
|
183
|
+
#
|
|
184
|
+
# @param name [String]
|
|
185
|
+
# @return [Module, Class, nil]
|
|
186
|
+
def safe_const_get(name)
|
|
187
|
+
Object.const_get(name)
|
|
188
|
+
rescue NameError
|
|
189
|
+
nil
|
|
159
190
|
end
|
|
160
191
|
end
|
|
161
192
|
end
|
|
@@ -5,29 +5,27 @@ require_relative 'rails'
|
|
|
5
5
|
module Verikloak
|
|
6
6
|
# Module providing Verikloak BFF (Backend for Frontend) functionality
|
|
7
7
|
module BFF
|
|
8
|
-
# Railtie
|
|
8
|
+
# Railtie for integrating Verikloak BFF with Rails applications.
|
|
9
9
|
#
|
|
10
|
-
# This
|
|
11
|
-
#
|
|
12
|
-
#
|
|
10
|
+
# This Railtie provides access to the BFF installation generator instead of
|
|
11
|
+
# automatically inserting middleware, which could cause boot failures when
|
|
12
|
+
# core Verikloak middleware is not yet configured.
|
|
13
13
|
#
|
|
14
|
-
# @example
|
|
15
|
-
#
|
|
16
|
-
# gem 'verikloak-bff'
|
|
14
|
+
# @example Installing BFF middleware
|
|
15
|
+
# rails g verikloak:bff:install
|
|
17
16
|
#
|
|
18
17
|
# @see Verikloak::BFF::Rails::Middleware
|
|
19
18
|
class Railtie < ::Rails::Railtie
|
|
20
|
-
#
|
|
19
|
+
# Loads the install generator when Rails generator infrastructure is available.
|
|
21
20
|
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# Uses Rails.logger as the logger if available.
|
|
21
|
+
# Makes the `verikloak:bff:install` generator discoverable through `rails g`
|
|
22
|
+
# while keeping generators optional for non-Rails environments.
|
|
25
23
|
#
|
|
26
|
-
# @
|
|
27
|
-
initializer 'verikloak.bff.
|
|
28
|
-
|
|
24
|
+
# @return [void]
|
|
25
|
+
initializer 'verikloak.bff.load_generators' do
|
|
26
|
+
next unless defined?(Rails::Generators)
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
require_relative '../../generators/verikloak/bff/install/install_generator'
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
31
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: verikloak-bff
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- taiyaky
|
|
@@ -78,6 +78,8 @@ files:
|
|
|
78
78
|
- CHANGELOG.md
|
|
79
79
|
- LICENSE
|
|
80
80
|
- README.md
|
|
81
|
+
- lib/generators/verikloak/bff/install/install_generator.rb
|
|
82
|
+
- lib/generators/verikloak/bff/install/templates/initializer.rb.erb
|
|
81
83
|
- lib/verikloak-bff.rb
|
|
82
84
|
- lib/verikloak/bff.rb
|
|
83
85
|
- lib/verikloak/bff/configuration.rb
|
|
@@ -86,6 +88,7 @@ files:
|
|
|
86
88
|
- lib/verikloak/bff/errors.rb
|
|
87
89
|
- lib/verikloak/bff/forwarded_token.rb
|
|
88
90
|
- lib/verikloak/bff/header_guard.rb
|
|
91
|
+
- lib/verikloak/bff/jwt_utils.rb
|
|
89
92
|
- lib/verikloak/bff/proxy_trust.rb
|
|
90
93
|
- lib/verikloak/bff/rails.rb
|
|
91
94
|
- lib/verikloak/bff/railtie.rb
|
|
@@ -98,7 +101,7 @@ metadata:
|
|
|
98
101
|
source_code_uri: https://github.com/taiyaky/verikloak-bff
|
|
99
102
|
changelog_uri: https://github.com/taiyaky/verikloak-bff/blob/main/CHANGELOG.md
|
|
100
103
|
bug_tracker_uri: https://github.com/taiyaky/verikloak-bff/issues
|
|
101
|
-
documentation_uri: https://rubydoc.info/gems/verikloak-bff/0.2.
|
|
104
|
+
documentation_uri: https://rubydoc.info/gems/verikloak-bff/0.2.4
|
|
102
105
|
rubygems_mfa_required: 'true'
|
|
103
106
|
rdoc_options: []
|
|
104
107
|
require_paths:
|