verikloak-pundit 0.1.0 → 0.1.1
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 +11 -0
- data/README.md +26 -2
- data/lib/verikloak/pundit/configuration.rb +148 -3
- data/lib/verikloak/pundit/controller.rb +4 -1
- data/lib/verikloak/pundit/version.rb +1 -1
- data/lib/verikloak/pundit.rb +20 -4
- 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: 9062ddd03a6b118971608756c82d1299dc4b509d7219d289aefbeaf381c8e49e
|
|
4
|
+
data.tar.gz: f2c2a0e9a236c454aabd9316b04ee6e1e98860f6d2db3d8e3863fb826d92a29f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b2a830375513deb2e8d4b350ffb7960fb95a6c58d5b45af7c519b45cd6086ffd21230af26a89921fca57528e48275e4bf4be59d96a2c45794a11d262c7fe80c3
|
|
7
|
+
data.tar.gz: deebae419df84ced4bb9ff0a9181847155db0adeb08f6b8cfb8319c6b2f50cd99cbd8e2dc2fa89417d15f2ab198c8e262503be630ab9ec3daf1ca58c5de7aeef
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.1.1] - 2025-09-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Optional exposure flag for the Rails helper (`expose_helper_method`) so claims can stay hidden from views when not needed.
|
|
14
|
+
- Continuous integration job that installs the latest `verikloak` and `verikloak-rails` releases and executes `integration/check.rb` to verify compatibility.
|
|
15
|
+
- Detailed operational guidance in README/ERRORS explaining the risks of `permission_role_scope = :all_resources` and helper exposure.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Configuration publishing now duplicates nested structures and freezes them, reducing race conditions when reconfiguring at runtime.
|
|
19
|
+
- `integration/check.rb` now exercises realm/resource roles, permission scopes, and helper exposure to catch regressions early.
|
|
20
|
+
|
|
10
21
|
## [0.1.0] - 2025-09-20
|
|
11
22
|
|
|
12
23
|
### Added
|
data/README.md
CHANGED
|
@@ -11,8 +11,6 @@ Pundit integration for the **Verikloak** family. This gem maps **Keycloak roles*
|
|
|
11
11
|
- Provides a `pundit_user` hook so policies can use `user.has_role?(:admin)` etc.
|
|
12
12
|
- Keeps role mapping **configurable** (project-specific mappings differ).
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
14
|
## Features
|
|
17
15
|
|
|
18
16
|
- **UserContext**: lightweight wrapper around JWT claims
|
|
@@ -88,7 +86,12 @@ Verikloak::Pundit.configure do |c|
|
|
|
88
86
|
# Permission mapping scope for `user.has_permission?`:
|
|
89
87
|
# :default_resource => realm roles + default client roles (recommended)
|
|
90
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
91
|
c.permission_role_scope = :default_resource
|
|
92
|
+
|
|
93
|
+
# Expose `verikloak_claims` to views via helper_method (Rails only)
|
|
94
|
+
c.expose_helper_method = true
|
|
92
95
|
end
|
|
93
96
|
```
|
|
94
97
|
|
|
@@ -114,12 +117,33 @@ docker compose run --rm dev rspec
|
|
|
114
117
|
docker compose run --rm dev rubocop -a
|
|
115
118
|
```
|
|
116
119
|
|
|
120
|
+
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:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
docker compose run --rm -e BUNDLE_FROZEN=0 dev bash -lc '
|
|
124
|
+
cd integration && \
|
|
125
|
+
apk add --no-cache --virtual .integration-build-deps \
|
|
126
|
+
build-base \
|
|
127
|
+
linux-headers \
|
|
128
|
+
openssl-dev \
|
|
129
|
+
yaml-dev && \
|
|
130
|
+
bundle config set --local path vendor/bundle && \
|
|
131
|
+
bundle install --jobs 4 --retry 3 && \
|
|
132
|
+
bundle exec ruby check.rb && \
|
|
133
|
+
apk del .integration-build-deps
|
|
134
|
+
'
|
|
135
|
+
```
|
|
136
|
+
|
|
117
137
|
## Contributing
|
|
118
138
|
Bug reports and pull requests are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
119
139
|
|
|
120
140
|
## Security
|
|
121
141
|
If you find a security vulnerability, please follow the instructions in [SECURITY.md](SECURITY.md).
|
|
122
142
|
|
|
143
|
+
### Operational guidance
|
|
144
|
+
- 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.
|
|
145
|
+
- 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
|
+
|
|
123
147
|
## License
|
|
124
148
|
This project is licensed under the [MIT License](LICENSE).
|
|
125
149
|
|
|
@@ -16,13 +16,62 @@ 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 expose_helper_method
|
|
20
|
+
# @return [Boolean] whether to register `verikloak_claims` as a Rails helper method
|
|
19
21
|
class Configuration
|
|
20
22
|
attr_accessor :resource_client, :role_map, :env_claims_key,
|
|
21
23
|
:realm_roles_path, :resource_roles_path,
|
|
22
|
-
:permission_role_scope
|
|
24
|
+
:permission_role_scope, :expose_helper_method
|
|
23
25
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
+
# Build a new configuration, optionally copying values from another
|
|
27
|
+
# configuration so callers can mutate a safe duplicate.
|
|
28
|
+
#
|
|
29
|
+
# @param copy_from [Configuration, nil]
|
|
30
|
+
def initialize(copy_from = nil)
|
|
31
|
+
if copy_from
|
|
32
|
+
initialize_from(copy_from)
|
|
33
|
+
else
|
|
34
|
+
initialize_defaults
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Create a deep-ish copy that can be safely mutated without affecting the
|
|
39
|
+
# source configuration. `dup` is overridden so the object returned from
|
|
40
|
+
# `Verikloak::Pundit.config.dup` behaves as expected.
|
|
41
|
+
#
|
|
42
|
+
# @return [Configuration]
|
|
43
|
+
def dup
|
|
44
|
+
self.class.new(self)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Duplicate the configuration via Ruby's `dup`, ensuring the new instance
|
|
48
|
+
# receives freshly-copied nested state.
|
|
49
|
+
#
|
|
50
|
+
# @param other [Configuration]
|
|
51
|
+
def initialize_copy(other)
|
|
52
|
+
super
|
|
53
|
+
initialize_from(other)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Freeze the configuration and its nested structures to prevent runtime
|
|
57
|
+
# mutations once it is published to the global state. Returns `self` to
|
|
58
|
+
# allow chaining inside callers.
|
|
59
|
+
#
|
|
60
|
+
# @return [Configuration]
|
|
61
|
+
def finalize!
|
|
62
|
+
@resource_client = freeze_string(@resource_client)
|
|
63
|
+
@env_claims_key = freeze_string(@env_claims_key)
|
|
64
|
+
@role_map = dup_hash(@role_map).freeze
|
|
65
|
+
@realm_roles_path = dup_array(@realm_roles_path).freeze
|
|
66
|
+
@resource_roles_path = dup_array(@resource_roles_path).freeze
|
|
67
|
+
@expose_helper_method = !@expose_helper_method.nil? && @expose_helper_method
|
|
68
|
+
freeze
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# Populate default values that mirror the gem's out-of-the-box behavior.
|
|
74
|
+
def initialize_defaults
|
|
26
75
|
@resource_client = 'rails-api'
|
|
27
76
|
@role_map = {} # e.g., { admin: :manage_all }
|
|
28
77
|
@env_claims_key = 'verikloak.user'
|
|
@@ -32,6 +81,102 @@ module Verikloak
|
|
|
32
81
|
# rubocop:enable Style/SymbolProc
|
|
33
82
|
# :default_resource (realm + default client), :all_resources (realm + all clients)
|
|
34
83
|
@permission_role_scope = :default_resource
|
|
84
|
+
@expose_helper_method = true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Copy configuration fields from another instance, duplicating mutable
|
|
88
|
+
# structures so future writes do not leak across instances.
|
|
89
|
+
#
|
|
90
|
+
# @param other [Configuration]
|
|
91
|
+
def initialize_from(other)
|
|
92
|
+
@resource_client = dup_string(other.resource_client)
|
|
93
|
+
@role_map = dup_hash(other.role_map)
|
|
94
|
+
@env_claims_key = dup_string(other.env_claims_key)
|
|
95
|
+
@realm_roles_path = dup_array(other.realm_roles_path)
|
|
96
|
+
@resource_roles_path = dup_array(other.resource_roles_path)
|
|
97
|
+
@permission_role_scope = other.permission_role_scope
|
|
98
|
+
@expose_helper_method = other.expose_helper_method
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Duplicate and freeze a string value, returning `nil` when appropriate.
|
|
102
|
+
#
|
|
103
|
+
# @param value [String, nil]
|
|
104
|
+
# @return [String, nil]
|
|
105
|
+
def freeze_string(value)
|
|
106
|
+
return nil if value.nil?
|
|
107
|
+
|
|
108
|
+
dup_string(value).freeze
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Recursively duplicate a hash, cloning nested structures so the copy can
|
|
112
|
+
# be mutated safely.
|
|
113
|
+
#
|
|
114
|
+
# @param value [Hash, nil]
|
|
115
|
+
# @return [Hash, nil]
|
|
116
|
+
def dup_hash(value)
|
|
117
|
+
return nil if value.nil?
|
|
118
|
+
|
|
119
|
+
copy = value.dup
|
|
120
|
+
copy.each do |key, element|
|
|
121
|
+
copy[key] =
|
|
122
|
+
case element
|
|
123
|
+
when Hash
|
|
124
|
+
dup_hash(element)
|
|
125
|
+
when Array
|
|
126
|
+
dup_array(element)
|
|
127
|
+
when String
|
|
128
|
+
dup_string(element)
|
|
129
|
+
else
|
|
130
|
+
duplicable?(element) ? element.dup : element
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
copy
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Duplicate a string guardingly, returning `nil` when no value is present.
|
|
137
|
+
#
|
|
138
|
+
# @param value [String, nil]
|
|
139
|
+
# @return [String, nil]
|
|
140
|
+
def dup_string(value)
|
|
141
|
+
return nil if value.nil?
|
|
142
|
+
|
|
143
|
+
value.dup
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Recursively duplicate an array while copying nested structures.
|
|
147
|
+
#
|
|
148
|
+
# @param value [Array, nil]
|
|
149
|
+
# @return [Array, nil]
|
|
150
|
+
def dup_array(value)
|
|
151
|
+
return nil if value.nil?
|
|
152
|
+
|
|
153
|
+
copy = value.dup
|
|
154
|
+
return copy unless copy.respond_to?(:map)
|
|
155
|
+
|
|
156
|
+
copy.map do |element|
|
|
157
|
+
case element
|
|
158
|
+
when Hash
|
|
159
|
+
dup_hash(element)
|
|
160
|
+
when Array
|
|
161
|
+
dup_array(element)
|
|
162
|
+
when String
|
|
163
|
+
dup_string(element)
|
|
164
|
+
else
|
|
165
|
+
duplicable?(element) ? element.dup : element
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Check whether a value can be safely duplicated using `dup`.
|
|
171
|
+
#
|
|
172
|
+
# @param value [Object]
|
|
173
|
+
# @return [Boolean]
|
|
174
|
+
def duplicable?(value)
|
|
175
|
+
return false if value.nil?
|
|
176
|
+
return false if [true, false].include?(value)
|
|
177
|
+
return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc)
|
|
178
|
+
|
|
179
|
+
value.respond_to?(:dup)
|
|
35
180
|
end
|
|
36
181
|
end
|
|
37
182
|
end
|
|
@@ -7,7 +7,10 @@ module Verikloak
|
|
|
7
7
|
# Hook used by Rails to include helper methods in views when available.
|
|
8
8
|
# @param base [Class]
|
|
9
9
|
def self.included(base)
|
|
10
|
-
|
|
10
|
+
return unless base.respond_to?(:helper_method)
|
|
11
|
+
|
|
12
|
+
config = Verikloak::Pundit.config
|
|
13
|
+
base.helper_method :verikloak_claims if config.expose_helper_method
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
# Pundit hook returning the UserContext built from Rack env claims.
|
data/lib/verikloak/pundit.rb
CHANGED
|
@@ -19,16 +19,32 @@ module Verikloak
|
|
|
19
19
|
# @yield [Configuration] Yields the configuration instance for mutation.
|
|
20
20
|
# @return [Configuration] the current configuration after applying changes
|
|
21
21
|
def configure
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
new_config = nil
|
|
23
|
+
config_mutex.synchronize do
|
|
24
|
+
current = @config&.dup || Configuration.new
|
|
25
|
+
yield current if block_given?
|
|
26
|
+
new_config = current.finalize!
|
|
27
|
+
@config = new_config
|
|
28
|
+
end
|
|
29
|
+
new_config
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
# Access the current configuration without mutating it.
|
|
28
33
|
#
|
|
29
34
|
# @return [Configuration]
|
|
30
35
|
def config
|
|
31
|
-
|
|
36
|
+
config_mutex.synchronize do
|
|
37
|
+
@config ||= Configuration.new.finalize!
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# Mutex protecting configuration reads/writes to maintain thread safety.
|
|
44
|
+
#
|
|
45
|
+
# @return [Mutex]
|
|
46
|
+
def config_mutex
|
|
47
|
+
@config_mutex ||= Mutex.new
|
|
32
48
|
end
|
|
33
49
|
end
|
|
34
50
|
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.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- taiyaky
|
|
@@ -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.1.
|
|
95
|
+
documentation_uri: https://rubydoc.info/gems/verikloak-pundit/0.1.1
|
|
96
96
|
rubygems_mfa_required: 'true'
|
|
97
97
|
rdoc_options: []
|
|
98
98
|
require_paths:
|