verikloak-pundit 0.1.1 → 0.2.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 +15 -0
- data/README.md +27 -0
- data/lib/generators/verikloak/pundit/install/install_generator.rb +0 -1
- data/lib/verikloak/pundit/configuration.rb +19 -1
- data/lib/verikloak/pundit/user_context.rb +30 -12
- data/lib/verikloak/pundit/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6936cd5efb5ccaecf789b427db2f7cd09f358604ba334f592f3e5079ac6501c4
|
|
4
|
+
data.tar.gz: 37db6dc08acd9918bebb15bd8dbb32320e00661ffc81b1b40c85850ec1c0822c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 120391c41f3f769ce49d7bddd83bed04b010ea367d8b63ddedb5de4610f9f8a6600f29f56f84c929b2209e4c26d6825a3d774ae1f32c20f16dd6d53378d5f9bf
|
|
7
|
+
data.tar.gz: a6d8babf7f27a842105fcc9ba44a42f9c73a21c45eeb486339c9afddca8aa1a6df97720c944407945ebd02a7918455bbb235b3e74de39025594d50d8babf0014
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2025-09-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Allow `permission_role_scope = :all_resources` to respect the new
|
|
14
|
+
`permission_resource_clients` whitelist so only approved clients contribute
|
|
15
|
+
to permission checks.
|
|
16
|
+
- Document verikloak-bff and verikloak-audience integration patterns,
|
|
17
|
+
including `env_claims_key` examples and role naming guidance.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- `UserContext` now snapshots the configuration at initialization to keep
|
|
21
|
+
behavior consistent even if `Verikloak::Pundit.configure` runs mid-request.
|
|
22
|
+
- Bump the minimum `verikloak` runtime dependency to `>= 0.1.5` to pick up
|
|
23
|
+
client whitelist support.
|
|
24
|
+
|
|
10
25
|
## [0.1.1] - 2025-09-20
|
|
11
26
|
|
|
12
27
|
### Added
|
data/README.md
CHANGED
|
@@ -89,12 +89,35 @@ Verikloak::Pundit.configure do |c|
|
|
|
89
89
|
# (enabling this broadens permissions to every resource client;
|
|
90
90
|
# review the upstream role assignments before turning it on)
|
|
91
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
|
|
92
97
|
|
|
93
98
|
# Expose `verikloak_claims` to views via helper_method (Rails only)
|
|
94
99
|
c.expose_helper_method = true
|
|
95
100
|
end
|
|
96
101
|
```
|
|
97
102
|
|
|
103
|
+
### Working with other Verikloak gems
|
|
104
|
+
|
|
105
|
+
- **verikloak-bff**: When your Rails application sits behind the BFF, the access
|
|
106
|
+
token presented to verikloak-pundit typically originates from the BFF
|
|
107
|
+
(e.g. via the `x-verikloak-user` header). Make sure your Rack stack stores the
|
|
108
|
+
decoded claims under the same `env_claims_key` configured above (the default
|
|
109
|
+
`"verikloak.user"` works out of the box with `verikloak-bff >= 0.3`). If the
|
|
110
|
+
BFF issues tokens for multiple downstream services, set
|
|
111
|
+
`permission_resource_clients` to the limited list of clients whose roles should
|
|
112
|
+
affect Rails-side authorization to avoid accidentally inheriting permissions
|
|
113
|
+
meant for other services.
|
|
114
|
+
- **verikloak-audience**: Audience services often mint resource roles with a
|
|
115
|
+
service-specific prefix (for example, `audience-service:editor`). Align your
|
|
116
|
+
`role_map` keys with that naming convention so `user.has_permission?` resolves
|
|
117
|
+
correctly. If Audience adds its own client entry inside `resource_access`, add
|
|
118
|
+
that client id to `permission_resource_clients` when you need to consume those
|
|
119
|
+
roles from Rails.
|
|
120
|
+
|
|
98
121
|
## Non-Rails / custom usage
|
|
99
122
|
|
|
100
123
|
```ruby
|
|
@@ -142,6 +165,10 @@ If you find a security vulnerability, please follow the instructions in [SECURIT
|
|
|
142
165
|
|
|
143
166
|
### Operational guidance
|
|
144
167
|
- 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.
|
|
168
|
+
- Combine `permission_role_scope = :all_resources` with `permission_resource_clients`
|
|
169
|
+
to explicitly opt-in the clients that may contribute permissions. Leaving the
|
|
170
|
+
whitelist blank (the default) reverts to the legacy behavior of trusting
|
|
171
|
+
every client in the token.
|
|
145
172
|
- 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.
|
|
146
173
|
|
|
147
174
|
## License
|
|
@@ -11,7 +11,6 @@ module Verikloak
|
|
|
11
11
|
desc 'Creates Verikloak Pundit initializer and a base ApplicationPolicy (optional).'
|
|
12
12
|
|
|
13
13
|
# Skip creating application_policy.rb
|
|
14
|
-
# @return [Boolean]
|
|
15
14
|
class_option :skip_policy, type: :boolean, default: false, desc: 'Do not create application_policy.rb'
|
|
16
15
|
|
|
17
16
|
# Create the initializer file under config/initializers.
|
|
@@ -16,12 +16,16 @@ module Verikloak
|
|
|
16
16
|
# @return [Array<String,Proc>] path inside JWT claims to reach resource roles
|
|
17
17
|
# @!attribute permission_role_scope
|
|
18
18
|
# @return [Symbol] :default_resource or :all_resources for permission mapping scope
|
|
19
|
+
# @!attribute permission_resource_clients
|
|
20
|
+
# @return [Array<String>, nil] list of resource clients allowed when
|
|
21
|
+
# {#permission_role_scope} is `:all_resources`. `nil` permits every client.
|
|
19
22
|
# @!attribute expose_helper_method
|
|
20
23
|
# @return [Boolean] whether to register `verikloak_claims` as a Rails helper method
|
|
21
24
|
class Configuration
|
|
22
25
|
attr_accessor :resource_client, :role_map, :env_claims_key,
|
|
23
26
|
:realm_roles_path, :resource_roles_path,
|
|
24
|
-
:permission_role_scope, :
|
|
27
|
+
:permission_role_scope, :permission_resource_clients,
|
|
28
|
+
:expose_helper_method
|
|
25
29
|
|
|
26
30
|
# Build a new configuration, optionally copying values from another
|
|
27
31
|
# configuration so callers can mutate a safe duplicate.
|
|
@@ -64,6 +68,7 @@ module Verikloak
|
|
|
64
68
|
@role_map = dup_hash(@role_map).freeze
|
|
65
69
|
@realm_roles_path = dup_array(@realm_roles_path).freeze
|
|
66
70
|
@resource_roles_path = dup_array(@resource_roles_path).freeze
|
|
71
|
+
@permission_resource_clients = freeze_permission_clients(@permission_resource_clients)
|
|
67
72
|
@expose_helper_method = !@expose_helper_method.nil? && @expose_helper_method
|
|
68
73
|
freeze
|
|
69
74
|
end
|
|
@@ -81,6 +86,7 @@ module Verikloak
|
|
|
81
86
|
# rubocop:enable Style/SymbolProc
|
|
82
87
|
# :default_resource (realm + default client), :all_resources (realm + all clients)
|
|
83
88
|
@permission_role_scope = :default_resource
|
|
89
|
+
@permission_resource_clients = nil
|
|
84
90
|
@expose_helper_method = true
|
|
85
91
|
end
|
|
86
92
|
|
|
@@ -95,6 +101,7 @@ module Verikloak
|
|
|
95
101
|
@realm_roles_path = dup_array(other.realm_roles_path)
|
|
96
102
|
@resource_roles_path = dup_array(other.resource_roles_path)
|
|
97
103
|
@permission_role_scope = other.permission_role_scope
|
|
104
|
+
@permission_resource_clients = dup_array(other.permission_resource_clients)
|
|
98
105
|
@expose_helper_method = other.expose_helper_method
|
|
99
106
|
end
|
|
100
107
|
|
|
@@ -178,6 +185,17 @@ module Verikloak
|
|
|
178
185
|
|
|
179
186
|
value.respond_to?(:dup)
|
|
180
187
|
end
|
|
188
|
+
|
|
189
|
+
# Normalize and freeze the configured permission clients list.
|
|
190
|
+
#
|
|
191
|
+
# @param value [Array<String, Symbol>, nil]
|
|
192
|
+
# @return [Array<String>, nil]
|
|
193
|
+
def freeze_permission_clients(value)
|
|
194
|
+
array = dup_array(value)
|
|
195
|
+
return nil if array.nil?
|
|
196
|
+
|
|
197
|
+
array.compact.map(&:to_s).uniq.freeze
|
|
198
|
+
end
|
|
181
199
|
end
|
|
182
200
|
end
|
|
183
201
|
end
|
|
@@ -4,15 +4,17 @@ module Verikloak
|
|
|
4
4
|
module Pundit
|
|
5
5
|
# Lightweight wrapper around Keycloak claims for Pundit policies.
|
|
6
6
|
class UserContext
|
|
7
|
-
attr_reader :claims, :resource_client
|
|
7
|
+
attr_reader :claims, :resource_client, :config
|
|
8
8
|
|
|
9
9
|
# Create a new user context from JWT claims.
|
|
10
10
|
#
|
|
11
11
|
# @param claims [Hash] JWT claims issued by Keycloak
|
|
12
12
|
# @param resource_client [String] default resource client name for resource roles
|
|
13
|
-
|
|
13
|
+
# @param config [Verikloak::Pundit::Configuration] configuration snapshot to use
|
|
14
|
+
def initialize(claims, resource_client: nil, config: nil)
|
|
15
|
+
@config = config || Verikloak::Pundit.config
|
|
14
16
|
@claims = claims || {}
|
|
15
|
-
@resource_client = resource_client.to_s
|
|
17
|
+
@resource_client = (resource_client || @config.resource_client).to_s
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
# Subject identifier from claims.
|
|
@@ -30,7 +32,7 @@ module Verikloak
|
|
|
30
32
|
# Realm-level roles from claims based on configuration path.
|
|
31
33
|
# @return [Array<String>]
|
|
32
34
|
def realm_roles
|
|
33
|
-
path = resolve_path(
|
|
35
|
+
path = resolve_path(config.realm_roles_path)
|
|
34
36
|
Array(claims.dig(*path))
|
|
35
37
|
end
|
|
36
38
|
|
|
@@ -40,7 +42,7 @@ module Verikloak
|
|
|
40
42
|
# @return [Array<String>]
|
|
41
43
|
def resource_roles(client = resource_client)
|
|
42
44
|
client = client.to_s
|
|
43
|
-
path = resolve_path(
|
|
45
|
+
path = resolve_path(config.resource_roles_path, client: client)
|
|
44
46
|
Array(claims.dig(*path))
|
|
45
47
|
end
|
|
46
48
|
|
|
@@ -82,7 +84,7 @@ module Verikloak
|
|
|
82
84
|
def has_permission?(perm) # rubocop:disable Naming/PredicatePrefix
|
|
83
85
|
pr = perm.to_sym
|
|
84
86
|
roles = realm_roles + resource_roles_scope
|
|
85
|
-
mapped = roles.map { |r| RoleMapper.map(r,
|
|
87
|
+
mapped = roles.map { |r| RoleMapper.map(r, config) }
|
|
86
88
|
mapped.map(&:to_sym).include?(pr)
|
|
87
89
|
end
|
|
88
90
|
|
|
@@ -91,8 +93,9 @@ module Verikloak
|
|
|
91
93
|
# @param env [Hash] Rack environment
|
|
92
94
|
# @return [UserContext]
|
|
93
95
|
def self.from_env(env)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
config = Verikloak::Pundit.config
|
|
97
|
+
claims = env[config.env_claims_key]
|
|
98
|
+
new(claims, config: config)
|
|
96
99
|
end
|
|
97
100
|
|
|
98
101
|
private
|
|
@@ -108,9 +111,9 @@ module Verikloak
|
|
|
108
111
|
when Proc
|
|
109
112
|
# Support lambdas that accept (config) or (config, client)
|
|
110
113
|
if seg.arity >= 2
|
|
111
|
-
seg.call(
|
|
114
|
+
seg.call(config, client).to_s
|
|
112
115
|
else
|
|
113
|
-
seg.call(
|
|
116
|
+
seg.call(config).to_s
|
|
114
117
|
end
|
|
115
118
|
else
|
|
116
119
|
seg.to_s
|
|
@@ -121,7 +124,7 @@ module Verikloak
|
|
|
121
124
|
# Resolve resource roles based on configured permission scope.
|
|
122
125
|
# @return [Array<String>]
|
|
123
126
|
def resource_roles_scope
|
|
124
|
-
case
|
|
127
|
+
case config.permission_role_scope&.to_sym
|
|
125
128
|
when :all_resources
|
|
126
129
|
resource_roles_all_clients
|
|
127
130
|
else
|
|
@@ -137,7 +140,22 @@ module Verikloak
|
|
|
137
140
|
|
|
138
141
|
# Bypass configured path lambda (which targets the default client)
|
|
139
142
|
# and gather roles from all clients explicitly.
|
|
140
|
-
access.
|
|
143
|
+
access.each_with_object([]) do |(client_id, entry), roles|
|
|
144
|
+
next unless permission_client_allowed?(client_id)
|
|
145
|
+
|
|
146
|
+
roles.concat(Array(entry['roles']))
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Check whether the given client is allowed for permission scope.
|
|
151
|
+
#
|
|
152
|
+
# @param client_id [String]
|
|
153
|
+
# @return [Boolean]
|
|
154
|
+
def permission_client_allowed?(client_id)
|
|
155
|
+
whitelist = config.permission_resource_clients
|
|
156
|
+
return true if whitelist.nil?
|
|
157
|
+
|
|
158
|
+
whitelist.include?(client_id.to_s)
|
|
141
159
|
end
|
|
142
160
|
end
|
|
143
161
|
end
|
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.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- taiyaky
|
|
@@ -49,20 +49,20 @@ dependencies:
|
|
|
49
49
|
requirements:
|
|
50
50
|
- - ">="
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
|
-
version: 0.1.
|
|
52
|
+
version: 0.1.5
|
|
53
53
|
- - "<"
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
|
-
version:
|
|
55
|
+
version: 1.0.0
|
|
56
56
|
type: :runtime
|
|
57
57
|
prerelease: false
|
|
58
58
|
version_requirements: !ruby/object:Gem::Requirement
|
|
59
59
|
requirements:
|
|
60
60
|
- - ">="
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: 0.1.
|
|
62
|
+
version: 0.1.5
|
|
63
63
|
- - "<"
|
|
64
64
|
- !ruby/object:Gem::Version
|
|
65
|
-
version:
|
|
65
|
+
version: 1.0.0
|
|
66
66
|
description: Maps Keycloak JWT roles to a Pundit-friendly UserContext with helpers
|
|
67
67
|
and a Rails generator.
|
|
68
68
|
executables: []
|
|
@@ -92,7 +92,7 @@ metadata:
|
|
|
92
92
|
source_code_uri: https://github.com/taiyaky/verikloak-pundit
|
|
93
93
|
changelog_uri: https://github.com/taiyaky/verikloak-pundit/blob/main/CHANGELOG.md
|
|
94
94
|
bug_tracker_uri: https://github.com/taiyaky/verikloak-pundit/issues
|
|
95
|
-
documentation_uri: https://rubydoc.info/gems/verikloak-pundit/0.
|
|
95
|
+
documentation_uri: https://rubydoc.info/gems/verikloak-pundit/0.2.0
|
|
96
96
|
rubygems_mfa_required: 'true'
|
|
97
97
|
rdoc_options: []
|
|
98
98
|
require_paths:
|