Class: Verikloak::Pundit::Configuration
- Inherits:
-
Object
- Object
- Verikloak::Pundit::Configuration
- Defined in:
- lib/verikloak/pundit/configuration.rb
Overview
Runtime configuration for verikloak-pundit.
Instance Attribute Summary collapse
-
#env_claims_key ⇒ String
Rack env key where claims are stored (when using verikloak/verikloak-rails).
-
#expose_helper_method ⇒ Boolean
Whether
verikloak_claimsis exposed to Rails views. -
#permission_resource_clients ⇒ Array<String>?
List of resource clients allowed when #permission_role_scope is
:all_resources. -
#permission_role_scope ⇒ Symbol
:default_resource or :all_resources for permission mapping scope.
-
#realm_roles_path ⇒ Array<String,Proc>
Path inside JWT claims to reach realm roles.
-
#resource_client ⇒ String
Returns the resource client, falling back to ENV if not set.
-
#resource_roles_path ⇒ Array<String,Proc>
Path inside JWT claims to reach resource roles.
-
#role_map ⇒ Hash{Symbol=>Symbol,String,nil}
Mapping from roles to permissions; a nil value explicitly revokes the role's implicit permission.
-
#strict_permissions ⇒ Boolean
When true,
has_permission?only grants permissions that appear as values in #role_map; unmapped role names no longer act as implicit permissions.
Instance Method Summary collapse
-
#finalize! ⇒ Configuration
Freeze the configuration and its nested structures to prevent runtime mutations once it is published to the global state.
-
#initialize(copy_from = nil) ⇒ Configuration
constructor
Build a new configuration populated with default values, optionally copying values from another configuration so callers can mutate a safe duplicate.
-
#initialize_copy(other) ⇒ Object
Duplicate the configuration via Ruby's
dup/clone, ensuring the new instance receives freshly-copied (and unfrozen) nested state.
Constructor Details
#initialize(copy_from = nil) ⇒ Configuration
Build a new configuration populated with default values, optionally
copying values from another configuration so callers can mutate a
safe duplicate. The copy_from parameter is kept for backward
compatibility with the v1.0.0 signature; dup is the idiomatic way
to copy.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/verikloak/pundit/configuration.rb', line 66 def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end |
Instance Attribute Details
#env_claims_key ⇒ String
Returns Rack env key where claims are stored (when using verikloak/verikloak-rails).
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
#expose_helper_method ⇒ Boolean
Returns whether verikloak_claims is exposed to Rails views.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
#permission_resource_clients ⇒ Array<String>?
Returns list of resource clients allowed when
#permission_role_scope is :all_resources. nil permits every client.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
#permission_role_scope ⇒ Symbol
Returns :default_resource or :all_resources for permission mapping scope.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
#realm_roles_path ⇒ Array<String,Proc>
Returns path inside JWT claims to reach realm roles.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
#resource_client ⇒ String
Returns the resource client, falling back to ENV if not set.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
#resource_roles_path ⇒ Array<String,Proc>
Returns path inside JWT claims to reach resource roles.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
#role_map ⇒ Hash{Symbol=>Symbol,String,nil}
Returns mapping from roles to permissions; a nil value explicitly revokes the role's implicit permission.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
#strict_permissions ⇒ Boolean
Returns when true, has_permission? only grants permissions that
appear as values in #role_map; unmapped role names no longer act as
implicit permissions.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/verikloak/pundit/configuration.rb', line 30 class Configuration attr_accessor :env_claims_key, :realm_roles_path, :resource_roles_path, :permission_role_scope, :permission_resource_clients, :strict_permissions, :expose_helper_method attr_reader :role_map attr_writer :resource_client # Set the role map, normalizing keys to symbols for consistent lookup. # Values must be Symbols, Strings, or nil (an explicit nil revokes the # role's implicit permission); anything else raises at assignment time # so misconfiguration surfaces at boot instead of as silently missing # permissions. # # @param value [Hash] # @return [void] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def role_map=(value) @role_map = normalize_role_map(value) end # Returns the resource client, falling back to ENV['KEYCLOAK_RESOURCE_CLIENT'] if not set. # # @return [String] def resource_client @resource_client || ENV.fetch('KEYCLOAK_RESOURCE_CLIENT', 'rails-api') end # Build a new configuration populated with default values, optionally # copying values from another configuration so callers can mutate a # safe duplicate. The `copy_from` parameter is kept for backward # compatibility with the v1.0.0 signature; `dup` is the idiomatic way # to copy. # # @param copy_from [Configuration, nil] def initialize(copy_from = nil) @resource_client = nil # Falls back to ENV['KEYCLOAK_RESOURCE_CLIENT'] or 'rails-api' @role_map = {} # e.g., { admin: :manage_all } @env_claims_key = 'verikloak.user' @realm_roles_path = %w[realm_access roles] # The lambda receives (config, client) so that an explicitly requested # client (e.g. resource_role?(:other, :role)) resolves to that client's # entry instead of always falling back to the default resource client. @resource_roles_path = ['resource_access', ->(cfg, client) { client || cfg.resource_client }, 'roles'] # :default_resource (realm + default client), :all_resources (realm + all clients) @permission_role_scope = :default_resource @permission_resource_clients = nil @strict_permissions = false @expose_helper_method = true copy_state_from(copy_from) if copy_from end # Duplicate the configuration via Ruby's `dup`/`clone`, ensuring the new # instance receives freshly-copied (and unfrozen) nested state. # # @param other [Configuration] def initialize_copy(other) super copy_state_from(other) end # Freeze the configuration and its nested structures to prevent runtime # mutations once it is published to the global state. Returns `self` to # allow chaining inside callers. # # @return [Configuration] def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end private # Copy all configuration state from another instance, deep-duplicating # nested structures (and reading the raw @resource_client rather than # the getter, to preserve its ENV fallback behavior) so the copy can be # mutated without affecting the source. # # @param other [Configuration] # @return [void] def copy_state_from(other) @resource_client = deep_dup(other.instance_variable_get(:@resource_client)) @role_map = deep_dup(other.role_map) @env_claims_key = deep_dup(other.env_claims_key) @realm_roles_path = deep_dup(other.realm_roles_path) @resource_roles_path = deep_dup(other.resource_roles_path) @permission_role_scope = other. @permission_resource_clients = deep_dup(other.) @strict_permissions = other. @expose_helper_method = other.expose_helper_method end # Normalize role_map keys to symbols and validate value types. # # @param map [Hash, nil] # @return [Hash] # @raise [ArgumentError] when a value is neither Symbol, String, nor nil def normalize_role_map(map) return {} unless map.is_a?(Hash) map.each do |key, value| next if value.nil? || value.is_a?(Symbol) || value.is_a?(String) raise ArgumentError, "role_map value for #{key.inspect} must be a Symbol, a String, " \ "or nil (explicit revocation); got #{value.class}" end map.transform_keys(&:to_sym) end # Duplicate and freeze a string value, returning `nil` when appropriate. # # @param value [String, nil] # @return [String, nil] def freeze_string(value) return nil if value.nil? deep_dup(value).freeze end # Deep duplicate any object, handling nested structures recursively. # # @param value [Object] The value to duplicate # @return [Object] A deep copy of the value def deep_dup(value) case value when nil nil when Hash value.each_with_object({}) do |(key, element), copy| copy[deep_dup(key)] = deep_dup(element) end when Array value.map { |element| deep_dup(element) } when String value.dup else duplicable?(value) ? value.dup : value end end # Check whether a value can be safely duplicated using `dup`. # # @param value [Object] # @return [Boolean] def duplicable?(value) return false if value.nil? return false if [true, false].include?(value) return false if value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(Proc) value.respond_to?(:dup) end # Normalize and freeze the configured permission clients list. # # @param value [Array<String, Symbol>, nil] # @return [Array<String>, nil] def (value) array = deep_dup(value) return nil if array.nil? array.compact.map(&:to_s).uniq.freeze end end |
Instance Method Details
#finalize! ⇒ Configuration
Freeze the configuration and its nested structures to prevent runtime
mutations once it is published to the global state. Returns self to
allow chaining inside callers.
97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/verikloak/pundit/configuration.rb', line 97 def finalize! @resource_client = freeze_string(@resource_client) @env_claims_key = freeze_string(@env_claims_key) @role_map = deep_dup(@role_map).freeze @realm_roles_path = deep_dup(@realm_roles_path).freeze @resource_roles_path = deep_dup(@resource_roles_path).freeze @permission_resource_clients = (@permission_resource_clients) # Coerce flags to strict booleans based on truthiness @strict_permissions = @strict_permissions ? true : false @expose_helper_method = @expose_helper_method ? true : false freeze end |
#initialize_copy(other) ⇒ Object
Duplicate the configuration via Ruby's dup/clone, ensuring the new
instance receives freshly-copied (and unfrozen) nested state.
87 88 89 90 |
# File 'lib/verikloak/pundit/configuration.rb', line 87 def initialize_copy(other) super copy_state_from(other) end |