api_keys 0.4.0 → 0.4.2
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 +25 -1
- data/README.md +47 -0
- data/api_keys-0.4.1.gem +0 -0
- data/app/controllers/api_keys/application_controller.rb +53 -19
- data/lib/api_keys/configuration.rb +38 -0
- data/lib/api_keys/version.rb +1 -1
- data/lib/generators/api_keys/templates/add_authentication_index_to_api_keys.rb.erb +0 -8
- data/lib/generators/api_keys/templates/create_api_keys_table.rb.erb +0 -11
- data/lib/generators/api_keys/templates/initializer.rb +18 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1fb6244b37ca1c889e336a6d071bc2bb45fd30f02cb6284b6030c1c133665ce6
|
|
4
|
+
data.tar.gz: e51355f3d5a5f1f6b504b398b08e1e77d0b6c088eb53fdaf05866c4c58a2bbf4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1320fa52a52fa826e90c22948d8fa3fd7121f3c6b7248e01e21bc7285c8f165e62251a23633fc473655c92cc5183cc02e2121fb5e8effdbb408614bf48b0f9c7
|
|
7
|
+
data.tar.gz: 4129a55ee88634cb43d1004d7413d57bdf6d6e21bd9986ec9e86976b62c922ccd212f2c3e7ffd8527b4a3817d4dbdebba0444e14ad1fcc42b9796e1beb22f8ac
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
|
-
## [
|
|
1
|
+
## [0.4.2] - 2026-08-24
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- No library changes. Development-dependency and CI bumps (sqlite3 2.9.6 — clears GHSA-mwm8-39rw-8826 in the dummy app, thruster 0.1.25, brakeman 8.0.6, CodeQL and release-gem action pins).
|
|
6
|
+
- Release housekeeping: 0.4.1 shipped to RubyGems with the dashboard CSP fix, but its GitHub release page was lost to the repository's immutable-release rule during the cut and its tag points at a pre-squash commit (tree-identical to main). 0.4.2 consolidates: this tag is on main and this release page carries 0.4.1's notes below.
|
|
7
|
+
|
|
8
|
+
## [0.4.1] - 2026-08-24
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Stop the dashboard's Content Security Policy from blocking the host application's own stylesheets, scripts, and self-hosted webfonts. The 0.4.0 policy declared `default-src 'none'` with `script-src`/`style-src` reduced to the per-request nonce, but Rails only stamps that nonce onto `stylesheet_link_tag`/`javascript_include_tag` when the host application sets `content_security_policy_nonce_auto` (off by default, and not something an engine can enable for its host). Any application rendering its normal layout on engine pages got an unstyled, inert dashboard with no server-side error. The default policy now trusts same-origin scripts, styles, and fonts, adds `font-src`, and allows `https:` images, while keeping the nonce, `default-src 'self'`, `base-uri 'none'`, `object-src 'none'`, `frame-ancestors 'none'`, `frame-src 'none'`, `form-action 'self'`, and `connect-src 'self'`.
|
|
13
|
+
- Stop generated install and authentication-index migrations from carrying an unreachable `migration_version` instance method; the generator already renders the Active Record version into each migration superclass.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `config.dashboard_content_security_policy` selects the policy the mounted dashboard declares: `:default` (new default, hardened but compatible with a normal host layout), `:strict` (the 0.4.0 nonce-only policy, unchanged), or `false`/`nil` to declare nothing and leave the host application's policy alone. The setting is resolved per request and validated on assignment.
|
|
18
|
+
|
|
19
|
+
### Upgrade notes
|
|
20
|
+
|
|
21
|
+
- Applications that relied on the 0.4.0 nonce-only dashboard policy should set `config.dashboard_content_security_policy = :strict` to keep it. That policy requires a layout that serves nothing un-nonced on engine pages; the gem's built-in layout satisfies it, and host layouts additionally need `config.content_security_policy_nonce_auto = true`.
|
|
22
|
+
|
|
23
|
+
### Maintenance
|
|
24
|
+
|
|
25
|
+
- Move SimpleCov startup/reporting into the test helper and replace deprecated filtering/tracking APIs so the enforced line and branch coverage gates remain compatible with future SimpleCov releases.
|
|
2
26
|
|
|
3
27
|
## [0.4.0] - 2026-08-09
|
|
4
28
|
|
data/README.md
CHANGED
|
@@ -184,6 +184,8 @@ Tweak colors and spacing by overriding CSS variables in your application's style
|
|
|
184
184
|
}
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
+
Engine pages ship an enforcing Content Security Policy. The default allows same-origin scripts, styles, and fonts, so your own stylesheet loads normally — see [Dashboard Content Security Policy](#dashboard-content-security-policy) to tighten or disable it.
|
|
188
|
+
|
|
187
189
|
#### Building Custom Integrations
|
|
188
190
|
|
|
189
191
|
If you need complete control over the UI (e.g., to match your design system with Tailwind, Bootstrap, etc.), you can build your own views and controllers while using the gem's model layer and helpers.
|
|
@@ -973,6 +975,51 @@ Increase the TTL to reduce repeated lookup work without making cached state auth
|
|
|
973
975
|
config.cache_ttl = 2.minutes
|
|
974
976
|
```
|
|
975
977
|
|
|
978
|
+
### Dashboard Content Security Policy
|
|
979
|
+
|
|
980
|
+
The mounted dashboard renders credential material, so it declares its own enforcing `Content-Security-Policy` on engine pages even when your application has none. You choose how strict it is:
|
|
981
|
+
|
|
982
|
+
```ruby
|
|
983
|
+
config.dashboard_content_security_policy = :default # :default (default), :strict, or false/nil
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
| Value | Behavior |
|
|
987
|
+
|-------|----------|
|
|
988
|
+
| `:default` | Hardened, but compatible with a normal host layout. |
|
|
989
|
+
| `:strict` | Nonce-only: nothing loads unless it carries the engine's per-request nonce. |
|
|
990
|
+
| `false` / `nil` | The gem declares nothing; your application's policy applies unchanged. |
|
|
991
|
+
|
|
992
|
+
**`:default`** emits (nonce regenerated per request):
|
|
993
|
+
|
|
994
|
+
```
|
|
995
|
+
base-uri 'none'; object-src 'none'; frame-ancestors 'none'; frame-src 'none';
|
|
996
|
+
form-action 'self'; connect-src 'self'; default-src 'self';
|
|
997
|
+
script-src 'self' 'nonce-…'; style-src 'self' 'nonce-…';
|
|
998
|
+
font-src 'self' data:; img-src 'self' https: data:
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
Framing, plugins, `<base>` hijacking, and cross-origin form posts stay blocked, and the dashboard's own inline `<style>`/`<script>` blocks stay nonce-gated — but your layout's stylesheets, scripts, and self-hosted webfonts still load.
|
|
1002
|
+
|
|
1003
|
+
**`:strict`** restores the nonce-only policy: `default-src 'none'` with no source expression for scripts or styles, so the per-request nonce is the only thing that can run.
|
|
1004
|
+
|
|
1005
|
+
```ruby
|
|
1006
|
+
config.dashboard_content_security_policy = :strict
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
Only use `:strict` if the layout rendered on engine pages serves nothing un-nonced. Rails does **not** put the nonce on `stylesheet_link_tag` / `javascript_include_tag` unless the host application opts in:
|
|
1010
|
+
|
|
1011
|
+
```ruby
|
|
1012
|
+
# config/application.rb — required for :strict with a normal host layout
|
|
1013
|
+
config.content_security_policy_nonce_generator = ->(request) { SecureRandom.base64(16) }
|
|
1014
|
+
config.content_security_policy_nonce_auto = true
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
Without that, `:strict` blocks your layout's own asset tags and the dashboard renders unstyled and inert. The gem's built-in layout is fully self-contained and works under `:strict` as-is.
|
|
1018
|
+
|
|
1019
|
+
**`false` / `nil`** leaves your application's policy completely alone. If your policy is nonce-based, allow the dashboard's nonced inline `<style>` and `<script>` yourself.
|
|
1020
|
+
|
|
1021
|
+
Any directive the gem does not set is inherited from your application's policy.
|
|
1022
|
+
|
|
976
1023
|
|
|
977
1024
|
## Callbacks: analytics, logging, usage monitoring & auditing
|
|
978
1025
|
|
data/api_keys-0.4.1.gem
ADDED
|
Binary file
|
|
@@ -18,25 +18,11 @@ module ApiKeys
|
|
|
18
18
|
# The dashboard renders credential material, so give it an enforcing,
|
|
19
19
|
# self-contained CSP even when the host application has not configured one.
|
|
20
20
|
# Preserve any host directives we do not explicitly tighten.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
policy.object_src :none
|
|
27
|
-
policy.frame_ancestors :none
|
|
28
|
-
policy.frame_src :none
|
|
29
|
-
policy.form_action :self
|
|
30
|
-
# Rails appends the per-request nonce to these directives. `none` leaves
|
|
31
|
-
# the nonce as the only effective source instead of trusting every
|
|
32
|
-
# same-origin script or stylesheet.
|
|
33
|
-
policy.script_src :none
|
|
34
|
-
policy.style_src :none
|
|
35
|
-
policy.img_src :self, :data
|
|
36
|
-
policy.connect_src :self
|
|
37
|
-
end
|
|
38
|
-
content_security_policy_report_only false
|
|
39
|
-
end
|
|
21
|
+
#
|
|
22
|
+
# The policy is selected by `ApiKeys.configuration.dashboard_content_security_policy`
|
|
23
|
+
# and resolved per request, so host applications can relax or disable it from
|
|
24
|
+
# an initializer. See `apply_api_keys_content_security_policy` below.
|
|
25
|
+
before_action :apply_api_keys_content_security_policy
|
|
40
26
|
|
|
41
27
|
# Ensure the owner is authenticated for all actions within this engine
|
|
42
28
|
# This uses the configured authentication method (defaults to authenticate_user!)
|
|
@@ -45,6 +31,54 @@ module ApiKeys
|
|
|
45
31
|
|
|
46
32
|
private
|
|
47
33
|
|
|
34
|
+
# Declares the dashboard's Content Security Policy for this request.
|
|
35
|
+
#
|
|
36
|
+
# `:default` keeps every structural protection (no framing, no plugins, no
|
|
37
|
+
# `<base>` hijacking, same-origin form submission) while still trusting
|
|
38
|
+
# same-origin scripts, styles, and fonts. That matters because Rails only
|
|
39
|
+
# stamps the per-request nonce onto `stylesheet_link_tag` / `javascript_include_tag`
|
|
40
|
+
# when the host sets `config.content_security_policy_nonce_auto`, which is off
|
|
41
|
+
# by default and which this engine cannot turn on for the host. Without a
|
|
42
|
+
# source expression the host layout's own asset tags are blocked and the
|
|
43
|
+
# dashboard renders unstyled and inert.
|
|
44
|
+
#
|
|
45
|
+
# `:strict` restores the nonce-only policy for applications whose layout
|
|
46
|
+
# serves nothing un-nonced on engine pages. `false`/`nil` declares nothing.
|
|
47
|
+
def apply_api_keys_content_security_policy
|
|
48
|
+
mode = ApiKeys.configuration.dashboard_content_security_policy
|
|
49
|
+
return unless mode # false/nil: leave the host application's policy alone
|
|
50
|
+
return unless request.respond_to?(:content_security_policy=)
|
|
51
|
+
|
|
52
|
+
prepare_api_keys_content_security_policy_nonce
|
|
53
|
+
|
|
54
|
+
policy = request.content_security_policy&.clone || ActionDispatch::ContentSecurityPolicy.new
|
|
55
|
+
policy.base_uri :none
|
|
56
|
+
policy.object_src :none
|
|
57
|
+
policy.frame_ancestors :none
|
|
58
|
+
policy.frame_src :none
|
|
59
|
+
policy.form_action :self
|
|
60
|
+
policy.connect_src :self
|
|
61
|
+
|
|
62
|
+
if mode == :strict
|
|
63
|
+
# Rails appends the per-request nonce to script-src/style-src. `none`
|
|
64
|
+
# leaves the nonce as the only effective source instead of trusting
|
|
65
|
+
# every same-origin script or stylesheet.
|
|
66
|
+
policy.default_src :none
|
|
67
|
+
policy.script_src :none
|
|
68
|
+
policy.style_src :none
|
|
69
|
+
policy.img_src :self, :data
|
|
70
|
+
else
|
|
71
|
+
policy.default_src :self
|
|
72
|
+
policy.script_src :self
|
|
73
|
+
policy.style_src :self
|
|
74
|
+
policy.font_src :self, :data
|
|
75
|
+
policy.img_src :self, :https, :data
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
request.content_security_policy = policy
|
|
79
|
+
request.content_security_policy_report_only = false if request.respond_to?(:content_security_policy_report_only=)
|
|
80
|
+
end
|
|
81
|
+
|
|
48
82
|
def prepare_api_keys_content_security_policy_nonce
|
|
49
83
|
return unless request.respond_to?(:content_security_policy_nonce_generator=)
|
|
50
84
|
|
|
@@ -56,6 +56,20 @@ module ApiKeys
|
|
|
56
56
|
# Engine UI Configuration
|
|
57
57
|
attr_accessor :return_url, :return_text
|
|
58
58
|
|
|
59
|
+
# Dashboard Content Security Policy
|
|
60
|
+
#
|
|
61
|
+
# @!attribute [rw] dashboard_content_security_policy
|
|
62
|
+
# @return [Symbol, false, nil] Which Content-Security-Policy the mounted
|
|
63
|
+
# dashboard declares for its own pages.
|
|
64
|
+
# - `:default` (default) — hardened but host-compatible: same-origin
|
|
65
|
+
# scripts, styles, and fonts are allowed alongside the engine's
|
|
66
|
+
# per-request nonce, so a host layout's asset tags keep working.
|
|
67
|
+
# - `:strict` — nonce-only: `default-src 'none'` with no source
|
|
68
|
+
# expression for scripts or styles. Requires a host layout that
|
|
69
|
+
# serves no un-nonced assets on engine pages.
|
|
70
|
+
# - `false` / `nil` — declare nothing and leave the host policy alone.
|
|
71
|
+
attr_reader :dashboard_content_security_policy
|
|
72
|
+
|
|
59
73
|
# Debugging
|
|
60
74
|
attr_reader :debug_logging
|
|
61
75
|
|
|
@@ -108,6 +122,7 @@ module ApiKeys
|
|
|
108
122
|
|
|
109
123
|
VALID_HASH_STRATEGIES = %i[sha256 bcrypt].freeze
|
|
110
124
|
VALID_TOKEN_ALPHABETS = %i[base58 hex].freeze
|
|
125
|
+
VALID_DASHBOARD_CONTENT_SECURITY_POLICIES = [:default, :strict, false, nil].freeze
|
|
111
126
|
TOKEN_LENGTH_RANGE = (16..64)
|
|
112
127
|
MAX_CONFIGURED_SCOPES = 100
|
|
113
128
|
CONFIG_NAME_PATTERN = /\A[a-zA-Z0-9_-]{1,64}\z/
|
|
@@ -294,6 +309,23 @@ module ApiKeys
|
|
|
294
309
|
@token_alphabet = value
|
|
295
310
|
end
|
|
296
311
|
|
|
312
|
+
# Selects the Content-Security-Policy the dashboard declares for its own pages.
|
|
313
|
+
# `true` is accepted as an alias for `:default`; Strings are normalized to Symbols.
|
|
314
|
+
def dashboard_content_security_policy=(value)
|
|
315
|
+
normalized = case value
|
|
316
|
+
when true then :default
|
|
317
|
+
when String then value.to_sym
|
|
318
|
+
else value
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
unless VALID_DASHBOARD_CONTENT_SECURITY_POLICIES.include?(normalized)
|
|
322
|
+
raise ArgumentError,
|
|
323
|
+
"dashboard_content_security_policy must be :default, :strict, false, or nil"
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
@dashboard_content_security_policy = normalized
|
|
327
|
+
end
|
|
328
|
+
|
|
297
329
|
def hash_strategy=(value)
|
|
298
330
|
unless VALID_HASH_STRATEGIES.include?(value)
|
|
299
331
|
raise ArgumentError, "hash_strategy must be one of: #{VALID_HASH_STRATEGIES.join(', ')}"
|
|
@@ -541,6 +573,12 @@ module ApiKeys
|
|
|
541
573
|
@return_url = "/" # Default fallback path
|
|
542
574
|
@return_text = "‹ Home" # Default link text
|
|
543
575
|
|
|
576
|
+
# Dashboard Content Security Policy
|
|
577
|
+
# Hardened, but compatible with a normal host layout: same-origin scripts,
|
|
578
|
+
# styles, and fonts load, and the engine's inline blocks stay nonce-gated.
|
|
579
|
+
# Use :strict for the nonce-only policy, or false/nil to declare nothing.
|
|
580
|
+
@dashboard_content_security_policy = :default
|
|
581
|
+
|
|
544
582
|
# Debugging
|
|
545
583
|
@debug_logging = false # Disable debug logging by default (warn and error get logged regardless of this)
|
|
546
584
|
|
data/lib/api_keys/version.rb
CHANGED
|
@@ -21,12 +21,4 @@ class AddAuthenticationIndexToApiKeys < ActiveRecord::Migration<%= migration_ver
|
|
|
21
21
|
options[:algorithm] = :concurrently if connection.adapter_name.downcase.include?("postgres")
|
|
22
22
|
remove_index :api_keys, **options
|
|
23
23
|
end
|
|
24
|
-
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
def migration_version
|
|
28
|
-
major = ActiveRecord::VERSION::MAJOR
|
|
29
|
-
minor = ActiveRecord::VERSION::MINOR
|
|
30
|
-
major >= 5 ? "[#{major}.#{minor}]" : ""
|
|
31
|
-
end
|
|
32
24
|
end
|
|
@@ -94,15 +94,4 @@ class CreateApiKeysTable < ActiveRecord::Migration<%= migration_version %>
|
|
|
94
94
|
# Fallback during initial setup or if connection isn't available
|
|
95
95
|
:text
|
|
96
96
|
end
|
|
97
|
-
|
|
98
|
-
# Provides the appropriate migration version syntax for the current Rails version.
|
|
99
|
-
def migration_version
|
|
100
|
-
major = ActiveRecord::VERSION::MAJOR
|
|
101
|
-
minor = ActiveRecord::VERSION::MINOR
|
|
102
|
-
if major >= 5
|
|
103
|
-
"[#{major}.#{minor}]"
|
|
104
|
-
else
|
|
105
|
-
""
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
97
|
end
|
|
@@ -58,6 +58,24 @@ ApiKeys.configure do |config|
|
|
|
58
58
|
# Default: "‹ Home"
|
|
59
59
|
# config.return_text = "‹ Back to Settings"
|
|
60
60
|
|
|
61
|
+
# Content Security Policy for the mounted dashboard pages.
|
|
62
|
+
#
|
|
63
|
+
# :default (default) - Hardened, but compatible with a normal host layout.
|
|
64
|
+
# Blocks framing, plugins, <base> hijacking, and
|
|
65
|
+
# cross-origin form posts, while allowing same-origin
|
|
66
|
+
# scripts, styles, and fonts so your layout's asset
|
|
67
|
+
# tags keep working.
|
|
68
|
+
# :strict - Nonce-only: no script or stylesheet loads unless it
|
|
69
|
+
# carries the engine's per-request nonce. Only use this
|
|
70
|
+
# if your layout serves nothing un-nonced on these pages
|
|
71
|
+
# (Rails only nonces asset tags when you also set
|
|
72
|
+
# config.content_security_policy_nonce_auto = true).
|
|
73
|
+
# false / nil - Declare nothing; your application's own policy applies
|
|
74
|
+
# unchanged. You then have to allow the dashboard's
|
|
75
|
+
# nonced inline <style>/<script> yourself.
|
|
76
|
+
#
|
|
77
|
+
# config.dashboard_content_security_policy = :default
|
|
78
|
+
|
|
61
79
|
# ============================================================================
|
|
62
80
|
# TOKEN PREFIXES
|
|
63
81
|
# ============================================================================
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: api_keys
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- rameerez
|
|
@@ -70,6 +70,7 @@ files:
|
|
|
70
70
|
- LICENSE.txt
|
|
71
71
|
- README.md
|
|
72
72
|
- SECURITY.md
|
|
73
|
+
- api_keys-0.4.1.gem
|
|
73
74
|
- api_keys_dashboard.webp
|
|
74
75
|
- api_keys_permissions.webp
|
|
75
76
|
- api_keys_token.webp
|