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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ece87ad9829b8123230d55d270c57bf399c3b16a5c8d38e7e89e3c2e31a3f63
4
- data.tar.gz: 511d923d115ebe33105099ab61b0390e46288e013ecca8a455c9b896abf9d5e7
3
+ metadata.gz: 9062ddd03a6b118971608756c82d1299dc4b509d7219d289aefbeaf381c8e49e
4
+ data.tar.gz: f2c2a0e9a236c454aabd9316b04ee6e1e98860f6d2db3d8e3863fb826d92a29f
5
5
  SHA512:
6
- metadata.gz: acf8d7d1a12e3b12ce152deb35c4c506adbb6a9002cbc30aa9293ed8c5577c838692795c27edc332802db689a5af9ae55dcf6e9b06abd18f8b3cf77e06f1a331
7
- data.tar.gz: 9a514dcdd539c3a9e1ef7abbcfb858df7a8899ecb71985972b8a9199d2153efd434f06e3cbb798daf5ea9a990c7e41b750f189dbf01b0149208ead90b0f39072
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
- # Initialize default configuration values.
25
- def initialize
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
- base.helper_method :verikloak_claims if base.respond_to?(:helper_method)
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.
@@ -5,6 +5,6 @@ module Verikloak
5
5
  # Gem version for verikloak-pundit.
6
6
  #
7
7
  # @return [String]
8
- VERSION = '0.1.0'
8
+ VERSION = '0.1.1'
9
9
  end
10
10
  end
@@ -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
- @config ||= Configuration.new
23
- yield @config if block_given?
24
- @config
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
- @config ||= Configuration.new
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.0
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.0
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: