verikloak-pundit 0.2.3 → 0.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/CHANGELOG.md +23 -0
- data/README.md +27 -57
- data/lib/generators/verikloak/pundit/install/templates/initializer.rb +23 -19
- data/lib/verikloak/pundit/configuration.rb +32 -3
- data/lib/verikloak/pundit/controller.rb +2 -1
- data/lib/verikloak/pundit/railtie.rb +29 -0
- data/lib/verikloak/pundit/user_context.rb +11 -2
- data/lib/verikloak/pundit/version.rb +1 -1
- 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: 8a9cbde30c7f580f43089a171707034fc1d476bdc63cf358ee00a7e2ceffe48c
|
|
4
|
+
data.tar.gz: 4bda6971e321a4a46045523f1dd01a610cb6a95b28e47a01c665d24a38bdaaf8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 26364d453900ef0bd915eedda4567343b65d8e9adfd7c61e02e926c954ad8bcc778042c7432ea4878a8fa1173a7f7eedccd5cea009f384b042044a65fadd53ca
|
|
7
|
+
data.tar.gz: 57494f3d1b8de679d2fb81b3a3ba7eff5d8635cadf0f4ef19110f8bd9d6bc70010629ed7c64b6584524c580e8968041d3c0581cc7d0468f067c13f45e4bdc254
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-01-01
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **ENV fallback preservation**: `Configuration#dup` now correctly preserves the `nil` state of `resource_client`, allowing ENV fallback to remain dynamic after duplication. Previously, duplicating a config would freeze the resolved ENV value.
|
|
14
|
+
- **Non-hash resource_access entries**: `resource_roles_all_clients` now guards against malformed `resource_access` entries that are not hashes, preventing potential `NoMethodError`.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **role_map key normalization**: `role_map` keys are now automatically normalized to symbols when set. This allows users to configure with string keys (e.g., from YAML) while maintaining consistent symbol-based lookup in `RoleMapper`.
|
|
18
|
+
- **pundit_user memoization**: `Controller#pundit_user` is now memoized with `@pundit_user ||=` to avoid creating multiple `UserContext` instances per request.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [0.2.4] - 2026-01-01
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- **Environment variable fallback**: `resource_client` now falls back to `ENV['KEYCLOAK_RESOURCE_CLIENT']` when not explicitly configured, enabling environment-based configuration.
|
|
26
|
+
- **Auto-sync with verikloak-rails**: When used alongside `verikloak-rails`, `env_claims_key` is automatically synchronized from `Verikloak::Rails.config.user_env_key` if not explicitly set.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- **Simplified initializer template**: Generator now produces a minimal initializer with commented examples instead of explicit defaults. Most settings work out of the box.
|
|
30
|
+
- **README improvements**: Updated Configuration section with environment variables table, auto-configuration documentation, and removed redundant examples.
|
|
31
|
+
- **Consistent Pundit include**: Quick Start examples now use `Pundit::Authorization` consistently.
|
|
32
|
+
|
|
10
33
|
## [0.2.3] - 2025-12-31
|
|
11
34
|
|
|
12
35
|
### Added
|
data/README.md
CHANGED
|
@@ -43,7 +43,7 @@ For error-handling guidance, see [ERRORS.md](ERRORS.md).
|
|
|
43
43
|
```ruby
|
|
44
44
|
# app/controllers/application_controller.rb
|
|
45
45
|
class ApplicationController < ActionController::API
|
|
46
|
-
include Pundit
|
|
46
|
+
include Pundit::Authorization
|
|
47
47
|
include Verikloak::Pundit::Controller # provides pundit_user
|
|
48
48
|
|
|
49
49
|
# If you're also using verikloak-rails:
|
|
@@ -65,38 +65,28 @@ Where `user` is the **UserContext** provided by `pundit_user`.
|
|
|
65
65
|
|
|
66
66
|
## Configuration
|
|
67
67
|
|
|
68
|
+
Most settings have sensible defaults and can be auto-configured. You only need to customize what's different for your application.
|
|
69
|
+
|
|
70
|
+
### Environment Variables
|
|
71
|
+
|
|
72
|
+
| Variable | Description | Default |
|
|
73
|
+
|----------|-------------|---------|
|
|
74
|
+
| `KEYCLOAK_RESOURCE_CLIENT` | Default resource client ID for resource roles | `"rails-api"` |
|
|
75
|
+
|
|
76
|
+
### Auto-configuration with verikloak-rails
|
|
77
|
+
|
|
78
|
+
When used alongside `verikloak-rails`, the following settings are automatically synchronized:
|
|
79
|
+
|
|
80
|
+
- **`env_claims_key`**: Inherits from `Verikloak::Rails.config.user_env_key` if you haven't explicitly set it. This ensures both gems read claims from the same Rack env key.
|
|
81
|
+
|
|
82
|
+
This means in most cases you can use a minimal initializer:
|
|
83
|
+
|
|
68
84
|
```ruby
|
|
69
|
-
# config/initializers/verikloak_pundit.rb
|
|
70
85
|
Verikloak::Pundit.configure do |c|
|
|
71
|
-
c.
|
|
72
|
-
c.role_map = { # optional role → permission mapping
|
|
86
|
+
c.role_map = {
|
|
73
87
|
admin: :manage_all,
|
|
74
|
-
editor: :write_notes
|
|
75
|
-
reader: :read_notes
|
|
88
|
+
editor: :write_notes
|
|
76
89
|
}
|
|
77
|
-
# Where to find claims in Rack env (when using verikloak/verikloak-rails)
|
|
78
|
-
c.env_claims_key = "verikloak.user"
|
|
79
|
-
|
|
80
|
-
# How to traverse JWT for roles
|
|
81
|
-
c.realm_roles_path = %w[realm_access roles] # => claims["realm_access"]["roles"]
|
|
82
|
-
# Lambdas in the path may accept (cfg) or (cfg, client)
|
|
83
|
-
# where `client` is the argument passed to `user.resource_roles(client)`
|
|
84
|
-
c.resource_roles_path = ["resource_access", ->(cfg){ cfg.resource_client }, "roles"]
|
|
85
|
-
|
|
86
|
-
# Permission mapping scope for `user.has_permission?`:
|
|
87
|
-
# :default_resource => realm roles + default client roles (recommended)
|
|
88
|
-
# :all_resources => realm roles + roles from all clients in resource_access
|
|
89
|
-
# (enabling this broadens permissions to every resource client;
|
|
90
|
-
# review the upstream role assignments before turning it on)
|
|
91
|
-
c.permission_role_scope = :default_resource
|
|
92
|
-
# Optional whitelist of resource clients when `permission_role_scope = :all_resources`.
|
|
93
|
-
# Leaving this as nil keeps the legacy "all clients" behavior, while providing
|
|
94
|
-
# an explicit list (e.g., %w[rails-api verikloak-bff]) limits which clients can
|
|
95
|
-
# contribute roles to permission checks.
|
|
96
|
-
c.permission_resource_clients = nil
|
|
97
|
-
|
|
98
|
-
# Expose `verikloak_claims` to views via helper_method (Rails only)
|
|
99
|
-
c.expose_helper_method = true
|
|
100
90
|
end
|
|
101
91
|
```
|
|
102
92
|
|
|
@@ -105,12 +95,11 @@ end
|
|
|
105
95
|
- **verikloak-bff**: When your Rails application sits behind the BFF, the access
|
|
106
96
|
token presented to verikloak-pundit typically originates from the BFF
|
|
107
97
|
(e.g. via the `x-verikloak-user` header). Make sure your Rack stack stores the
|
|
108
|
-
decoded claims under the same `env_claims_key`
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
meant for other services.
|
|
98
|
+
decoded claims under the same `env_claims_key` (default: `"verikloak.user"`,
|
|
99
|
+
which works out of the box with `verikloak-bff >= 0.3`). If the BFF issues
|
|
100
|
+
tokens for multiple downstream services, set `permission_resource_clients` to
|
|
101
|
+
the limited list of clients whose roles should affect Rails-side authorization
|
|
102
|
+
to avoid accidentally inheriting permissions meant for other services.
|
|
114
103
|
- **verikloak-audience**: Audience services often mint resource roles with a
|
|
115
104
|
service-specific prefix (for example, `audience-service:editor`). Align your
|
|
116
105
|
`role_map` keys with that naming convention so `user.has_permission?` resolves
|
|
@@ -319,35 +308,16 @@ RSpec.configure do |config|
|
|
|
319
308
|
end
|
|
320
309
|
```
|
|
321
310
|
|
|
322
|
-
An additional integration check exercises the gem together with the latest `verikloak` and `verikloak-rails` releases. This runs in CI automatically, and you can execute it locally with:
|
|
323
|
-
|
|
324
|
-
```bash
|
|
325
|
-
docker compose run --rm -e BUNDLE_FROZEN=0 dev bash -lc '
|
|
326
|
-
cd integration && \
|
|
327
|
-
apk add --no-cache --virtual .integration-build-deps \
|
|
328
|
-
build-base \
|
|
329
|
-
linux-headers \
|
|
330
|
-
openssl-dev \
|
|
331
|
-
yaml-dev && \
|
|
332
|
-
bundle config set --local path vendor/bundle && \
|
|
333
|
-
bundle install --jobs 4 --retry 3 && \
|
|
334
|
-
bundle exec ruby check.rb && \
|
|
335
|
-
apk del .integration-build-deps
|
|
336
|
-
'
|
|
337
|
-
```
|
|
338
|
-
|
|
339
311
|
## Contributing
|
|
340
312
|
Bug reports and pull requests are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
341
313
|
|
|
342
314
|
## Security
|
|
343
315
|
If you find a security vulnerability, please follow the instructions in [SECURITY.md](SECURITY.md).
|
|
344
316
|
|
|
345
|
-
|
|
317
|
+
## Operational Guidance
|
|
318
|
+
|
|
346
319
|
- Enabling `permission_role_scope = :all_resources` pulls roles from every Keycloak client in `resource_access`. Review the granted roles carefully to ensure you are not expanding permissions beyond what the application expects.
|
|
347
|
-
- Combine `permission_role_scope = :all_resources` with `permission_resource_clients`
|
|
348
|
-
to explicitly opt-in the clients that may contribute permissions. Leaving the
|
|
349
|
-
whitelist blank (the default) reverts to the legacy behavior of trusting
|
|
350
|
-
every client in the token.
|
|
320
|
+
- Combine `permission_role_scope = :all_resources` with `permission_resource_clients` to explicitly opt-in the clients that may contribute permissions. Leaving the whitelist blank (the default) reverts to the legacy behavior of trusting every client in the token.
|
|
351
321
|
- Leaving `expose_helper_method = true` exposes `verikloak_claims` to the Rails view layer. If the claims include personal or sensitive data, consider switching it to `false` and pass only the minimum required information through controller-provided helpers.
|
|
352
322
|
|
|
353
323
|
## License
|
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Verikloak::Pundit initializer
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
#
|
|
5
|
+
# Most settings are auto-configured from environment variables and verikloak-rails.
|
|
6
|
+
# Uncomment and customize only what you need to override.
|
|
7
|
+
#
|
|
8
|
+
# Environment variables supported:
|
|
9
|
+
# KEYCLOAK_RESOURCE_CLIENT - resource client ID (default: 'rails-api')
|
|
10
|
+
#
|
|
11
|
+
# When used with verikloak-rails, env_claims_key is automatically synchronized.
|
|
6
12
|
Verikloak::Pundit.configure do |c|
|
|
7
|
-
|
|
8
|
-
c.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# claims
|
|
18
|
-
c.
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# :all_resources => realm roles + roles from all clients in resource_access
|
|
23
|
-
c.permission_role_scope = :default_resource
|
|
13
|
+
# Resource client (optional - falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api')
|
|
14
|
+
# c.resource_client = ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api')
|
|
15
|
+
|
|
16
|
+
# Role to permission mapping (optional)
|
|
17
|
+
# c.role_map = {
|
|
18
|
+
# admin: :manage_all,
|
|
19
|
+
# editor: :write_notes,
|
|
20
|
+
# reader: :read_notes
|
|
21
|
+
# }
|
|
22
|
+
|
|
23
|
+
# Uncomment to customize JWT claims path and scope (usually not needed):
|
|
24
|
+
# c.env_claims_key = 'verikloak.user'
|
|
25
|
+
# c.realm_roles_path = %w[realm_access roles]
|
|
26
|
+
# c.resource_roles_path = ['resource_access', ->(cfg) { cfg.resource_client }, 'roles']
|
|
27
|
+
# c.permission_role_scope = :default_resource # or :all_resources
|
|
24
28
|
end
|
|
@@ -22,11 +22,29 @@ module Verikloak
|
|
|
22
22
|
# @!attribute expose_helper_method
|
|
23
23
|
# @return [Boolean] whether to register `verikloak_claims` as a Rails helper method
|
|
24
24
|
class Configuration
|
|
25
|
-
attr_accessor :
|
|
25
|
+
attr_accessor :env_claims_key,
|
|
26
26
|
:realm_roles_path, :resource_roles_path,
|
|
27
27
|
:permission_role_scope, :permission_resource_clients,
|
|
28
28
|
:expose_helper_method
|
|
29
29
|
|
|
30
|
+
attr_reader :role_map
|
|
31
|
+
attr_writer :resource_client
|
|
32
|
+
|
|
33
|
+
# Set the role map, normalizing keys to symbols for consistent lookup.
|
|
34
|
+
#
|
|
35
|
+
# @param value [Hash]
|
|
36
|
+
# @return [void]
|
|
37
|
+
def role_map=(value)
|
|
38
|
+
@role_map = normalize_role_map(value)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set.
|
|
42
|
+
#
|
|
43
|
+
# @return [String]
|
|
44
|
+
def resource_client
|
|
45
|
+
@resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api')
|
|
46
|
+
end
|
|
47
|
+
|
|
30
48
|
# Build a new configuration, optionally copying values from another
|
|
31
49
|
# configuration so callers can mutate a safe duplicate.
|
|
32
50
|
#
|
|
@@ -77,7 +95,7 @@ module Verikloak
|
|
|
77
95
|
|
|
78
96
|
# Populate default values that mirror the gem's out-of-the-box behavior.
|
|
79
97
|
def initialize_defaults
|
|
80
|
-
@resource_client = 'rails-api'
|
|
98
|
+
@resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api'
|
|
81
99
|
@role_map = {} # e.g., { admin: :manage_all }
|
|
82
100
|
@env_claims_key = 'verikloak.user'
|
|
83
101
|
@realm_roles_path = %w[realm_access roles]
|
|
@@ -90,12 +108,23 @@ module Verikloak
|
|
|
90
108
|
@expose_helper_method = true
|
|
91
109
|
end
|
|
92
110
|
|
|
111
|
+
# Normalize role_map keys to symbols for consistent lookup.
|
|
112
|
+
#
|
|
113
|
+
# @param map [Hash, nil]
|
|
114
|
+
# @return [Hash]
|
|
115
|
+
def normalize_role_map(map)
|
|
116
|
+
return {} unless map.is_a?(Hash)
|
|
117
|
+
|
|
118
|
+
map.transform_keys(&:to_sym)
|
|
119
|
+
end
|
|
120
|
+
|
|
93
121
|
# Copy configuration fields from another instance, duplicating mutable
|
|
94
122
|
# structures so future writes do not leak across instances.
|
|
95
123
|
#
|
|
96
124
|
# @param other [Configuration]
|
|
97
125
|
def initialize_from(other)
|
|
98
|
-
|
|
126
|
+
# Copy the raw instance variable, not the getter, to preserve ENV fallback behavior
|
|
127
|
+
@resource_client = dup_string(other.instance_variable_get(:@resource_client))
|
|
99
128
|
@role_map = dup_hash(other.role_map)
|
|
100
129
|
@env_claims_key = dup_string(other.env_claims_key)
|
|
101
130
|
@realm_roles_path = dup_array(other.realm_roles_path)
|
|
@@ -14,9 +14,10 @@ module Verikloak
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# Pundit hook returning the UserContext built from Rack env claims.
|
|
17
|
+
# Memoized to avoid creating multiple instances per request.
|
|
17
18
|
# @return [UserContext]
|
|
18
19
|
def pundit_user
|
|
19
|
-
Verikloak::Pundit::UserContext.from_env(request.env)
|
|
20
|
+
@pundit_user ||= Verikloak::Pundit::UserContext.from_env(request.env)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
# Access raw Verikloak claims from Rack env.
|
|
@@ -12,6 +12,35 @@ module Verikloak
|
|
|
12
12
|
include Verikloak::Pundit::Controller
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
# Synchronize configuration with verikloak-rails when available.
|
|
17
|
+
# Runs after verikloak-rails configuration is applied.
|
|
18
|
+
initializer 'verikloak_pundit.sync_configuration', after: 'verikloak.configure' do
|
|
19
|
+
Verikloak::Pundit::Railtie.sync_with_verikloak_rails if defined?(Verikloak::Rails)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# Sync env_claims_key with verikloak-rails configuration.
|
|
24
|
+
# Only applies if env_claims_key has not been explicitly set.
|
|
25
|
+
#
|
|
26
|
+
# @return [void]
|
|
27
|
+
def sync_with_verikloak_rails
|
|
28
|
+
return unless Verikloak::Rails.respond_to?(:config)
|
|
29
|
+
|
|
30
|
+
rails_config = Verikloak::Rails.config
|
|
31
|
+
return unless rails_config.respond_to?(:user_env_key) && rails_config.user_env_key
|
|
32
|
+
|
|
33
|
+
current_key = Verikloak::Pundit.config.env_claims_key
|
|
34
|
+
return unless current_key == 'verikloak.user'
|
|
35
|
+
|
|
36
|
+
# Use configure to properly update frozen config
|
|
37
|
+
Verikloak::Pundit.configure do |c|
|
|
38
|
+
c.env_claims_key = rails_config.user_env_key
|
|
39
|
+
end
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
warn "[verikloak-pundit] Failed to sync with verikloak-rails: #{e.message}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
15
44
|
end
|
|
16
45
|
end
|
|
17
46
|
end
|
|
@@ -43,7 +43,7 @@ module Verikloak
|
|
|
43
43
|
def realm_roles
|
|
44
44
|
@realm_roles ||= begin
|
|
45
45
|
path = resolve_path(config.realm_roles_path)
|
|
46
|
-
|
|
46
|
+
extract_roles(path)
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -55,7 +55,7 @@ module Verikloak
|
|
|
55
55
|
client = client.to_s
|
|
56
56
|
(@resource_roles_cache ||= {})[client] ||= begin
|
|
57
57
|
path = resolve_path(config.resource_roles_path, client: client)
|
|
58
|
-
|
|
58
|
+
extract_roles(path)
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -107,6 +107,14 @@ module Verikloak
|
|
|
107
107
|
|
|
108
108
|
private
|
|
109
109
|
|
|
110
|
+
# Extract roles from claims at the given path.
|
|
111
|
+
#
|
|
112
|
+
# @param path [Array<String>]
|
|
113
|
+
# @return [Array<String>]
|
|
114
|
+
def extract_roles(path)
|
|
115
|
+
Array(claims.dig(*path)).map(&:to_s).uniq.freeze
|
|
116
|
+
end
|
|
117
|
+
|
|
110
118
|
# Resolve a configured path into concrete dig segments.
|
|
111
119
|
#
|
|
112
120
|
# @param path_config [Array<String, Proc>]
|
|
@@ -146,6 +154,7 @@ module Verikloak
|
|
|
146
154
|
access = claims[CLAIM_RESOURCE_ACCESS]
|
|
147
155
|
if access.is_a?(Hash)
|
|
148
156
|
roles = access.each_with_object([]) do |(client_id, entry), acc|
|
|
157
|
+
next unless entry.is_a?(Hash)
|
|
149
158
|
next unless permission_client_allowed?(client_id)
|
|
150
159
|
|
|
151
160
|
acc.concat(Array(entry[CLAIM_ROLES]))
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: verikloak-pundit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- taiyaky
|
|
@@ -94,7 +94,7 @@ metadata:
|
|
|
94
94
|
source_code_uri: https://github.com/taiyaky/verikloak-pundit
|
|
95
95
|
changelog_uri: https://github.com/taiyaky/verikloak-pundit/blob/main/CHANGELOG.md
|
|
96
96
|
bug_tracker_uri: https://github.com/taiyaky/verikloak-pundit/issues
|
|
97
|
-
documentation_uri: https://rubydoc.info/gems/verikloak-pundit/0.
|
|
97
|
+
documentation_uri: https://rubydoc.info/gems/verikloak-pundit/0.3.0
|
|
98
98
|
rubygems_mfa_required: 'true'
|
|
99
99
|
rdoc_options: []
|
|
100
100
|
require_paths:
|