api_keys 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -0
- data/README.md +147 -38
- data/SECURITY.md +33 -0
- data/app/controllers/api_keys/application_controller.rb +92 -10
- data/app/controllers/api_keys/keys_controller.rb +40 -18
- data/app/views/api_keys/keys/_empty_state.html.erb +1 -1
- data/app/views/api_keys/keys/_form.html.erb +3 -3
- data/app/views/api_keys/keys/_key_actions.html.erb +3 -3
- data/app/views/api_keys/keys/_key_badges.html.erb +2 -2
- data/app/views/api_keys/keys/_key_row.html.erb +1 -1
- data/app/views/api_keys/keys/_key_status.html.erb +3 -3
- data/app/views/api_keys/keys/_keys_table.html.erb +1 -4
- data/app/views/api_keys/keys/_show_token.html.erb +5 -46
- data/app/views/api_keys/keys/_token_display.html.erb +3 -3
- data/app/views/api_keys/keys/index.html.erb +2 -2
- data/app/views/api_keys/keys/show.html.erb +2 -2
- data/app/views/api_keys/security/best_practices.html.erb +7 -7
- data/app/views/layouts/api_keys/application.html.erb +159 -12
- data/lib/api_keys/authentication.rb +39 -11
- data/lib/api_keys/configuration.rb +412 -24
- data/lib/api_keys/engine.rb +5 -20
- data/lib/api_keys/form_builder_extensions.rb +12 -2
- data/lib/api_keys/helpers/expiration_options.rb +11 -3
- data/lib/api_keys/helpers/token_session.rb +143 -8
- data/lib/api_keys/helpers/view_helpers.rb +5 -1
- data/lib/api_keys/jobs/callbacks_job.rb +10 -17
- data/lib/api_keys/jobs/update_stats_job.rb +27 -12
- data/lib/api_keys/models/api_key.rb +244 -25
- data/lib/api_keys/models/concerns/has_api_keys.rb +95 -32
- data/lib/api_keys/services/authenticator.rb +263 -118
- data/lib/api_keys/services/digestor.rb +76 -13
- data/lib/api_keys/services/token_generator.rb +41 -1
- data/lib/api_keys/tenant_resolution.rb +2 -4
- data/lib/api_keys/version.rb +1 -1
- data/lib/generators/api_keys/add_authentication_index_generator.rb +36 -0
- data/lib/generators/api_keys/templates/add_authentication_index_to_api_keys.rb.erb +24 -0
- data/lib/generators/api_keys/templates/create_api_keys_table.rb.erb +2 -14
- data/lib/generators/api_keys/templates/initializer.rb +54 -17
- metadata +16 -16
- data/.simplecov +0 -36
- data/AGENTS.md +0 -5
- data/Appraisals +0 -17
- data/CLAUDE.md +0 -5
- data/Rakefile +0 -37
- data/context7.json +0 -4
- data/gemfiles/rails_7.2.gemfile +0 -21
- data/gemfiles/rails_8.0.gemfile +0 -21
- data/gemfiles/rails_8.1.gemfile +0 -21
|
@@ -7,13 +7,25 @@ module ApiKeys
|
|
|
7
7
|
module Services
|
|
8
8
|
# Generates secure, random API tokens according to configured settings.
|
|
9
9
|
class TokenGenerator
|
|
10
|
+
MIN_RANDOM_BYTES = 16
|
|
11
|
+
MAX_RANDOM_BYTES = 64
|
|
12
|
+
MAX_PREFIX_BYTESIZE = 64
|
|
13
|
+
|
|
10
14
|
# Generates a new token string.
|
|
11
15
|
#
|
|
12
16
|
# @param length [Integer] The desired byte length of the random part (before encoding).
|
|
13
17
|
# @param prefix [String] The prefix to prepend to the token.
|
|
14
18
|
# @param alphabet [Symbol] The encoding alphabet (:base58 or :hex).
|
|
15
19
|
# @return [String] The generated token including the prefix.
|
|
16
|
-
def self.call(length:
|
|
20
|
+
def self.call(length: nil, prefix: nil, alphabet: nil)
|
|
21
|
+
length ||= ApiKeys.configuration.token_length
|
|
22
|
+
prefix ||= ApiKeys.configuration.resolved_token_prefix
|
|
23
|
+
alphabet ||= ApiKeys.configuration.token_alphabet
|
|
24
|
+
|
|
25
|
+
validate_length!(length)
|
|
26
|
+
validate_prefix!(prefix)
|
|
27
|
+
validate_alphabet!(alphabet)
|
|
28
|
+
|
|
17
29
|
random_bytes = SecureRandom.bytes(length)
|
|
18
30
|
|
|
19
31
|
random_part = case alphabet
|
|
@@ -27,6 +39,34 @@ module ApiKeys
|
|
|
27
39
|
|
|
28
40
|
"#{prefix}#{random_part}"
|
|
29
41
|
end
|
|
42
|
+
|
|
43
|
+
def self.validate_length!(length)
|
|
44
|
+
return if length.is_a?(Integer) && length.between?(MIN_RANDOM_BYTES, MAX_RANDOM_BYTES)
|
|
45
|
+
|
|
46
|
+
raise ArgumentError,
|
|
47
|
+
"Token length must be an Integer between #{MIN_RANDOM_BYTES} and #{MAX_RANDOM_BYTES} random bytes."
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.validate_prefix!(prefix)
|
|
51
|
+
valid = prefix.is_a?(String) && prefix.present? && prefix.valid_encoding? &&
|
|
52
|
+
prefix.bytesize <= MAX_PREFIX_BYTESIZE &&
|
|
53
|
+
prefix.each_codepoint.none? { |codepoint| codepoint <= 0x20 || codepoint == 0x7f }
|
|
54
|
+
return if valid
|
|
55
|
+
|
|
56
|
+
raise ArgumentError,
|
|
57
|
+
"Token prefix must be a non-blank string of at most #{MAX_PREFIX_BYTESIZE} bytes without whitespace or control characters."
|
|
58
|
+
rescue ArgumentError
|
|
59
|
+
raise ArgumentError,
|
|
60
|
+
"Token prefix must be a non-blank string of at most #{MAX_PREFIX_BYTESIZE} bytes without whitespace or control characters."
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.validate_alphabet!(alphabet)
|
|
64
|
+
return if %i[base58 hex].include?(alphabet)
|
|
65
|
+
|
|
66
|
+
raise ArgumentError, "Unsupported token alphabet: #{alphabet}. Use :base58 or :hex."
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private_class_method :validate_length!, :validate_prefix!, :validate_alphabet!
|
|
30
70
|
end
|
|
31
71
|
end
|
|
32
72
|
end
|
|
@@ -15,8 +15,6 @@ module ApiKeys
|
|
|
15
15
|
alias_method :current_api_key_tenant, :current_api_tenant
|
|
16
16
|
alias_method :current_api_account, :current_api_tenant
|
|
17
17
|
alias_method :current_api_key_account, :current_api_tenant
|
|
18
|
-
alias_method :current_api_owner, :current_api_tenant
|
|
19
|
-
alias_method :current_api_key_owner, :current_api_tenant
|
|
20
18
|
end
|
|
21
19
|
|
|
22
20
|
# Returns the tenant associated with the current API key.
|
|
@@ -31,10 +29,10 @@ module ApiKeys
|
|
|
31
29
|
|
|
32
30
|
resolver = ApiKeys.configuration.tenant_resolver
|
|
33
31
|
@current_api_tenant = resolver&.call(current_api_key)
|
|
34
|
-
rescue StandardError =>
|
|
32
|
+
rescue StandardError => error
|
|
35
33
|
# Log error but don't break the request if resolver fails
|
|
36
34
|
if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
|
37
|
-
Rails.logger.error "[ApiKeys] Tenant resolution failed
|
|
35
|
+
Rails.logger.error "[ApiKeys] Tenant resolution failed (#{error.class})."
|
|
38
36
|
end
|
|
39
37
|
@current_api_tenant = nil
|
|
40
38
|
end
|
data/lib/api_keys/version.rb
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module ApiKeys
|
|
7
|
+
module Generators
|
|
8
|
+
# Adds the bounded bcrypt authentication lookup index to existing installs.
|
|
9
|
+
class AddAuthenticationIndexGenerator < Rails::Generators::Base
|
|
10
|
+
include ActiveRecord::Generators::Migration
|
|
11
|
+
|
|
12
|
+
source_root File.expand_path("templates", __dir__)
|
|
13
|
+
|
|
14
|
+
def self.next_migration_number(dirname)
|
|
15
|
+
number = current_migration_number(dirname) + 1
|
|
16
|
+
ActiveRecord::Migration.next_migration_number(number)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_migration_file
|
|
20
|
+
migration_template "add_authentication_index_to_api_keys.rb.erb",
|
|
21
|
+
File.join(db_migrate_path, "add_authentication_index_to_api_keys.rb")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def display_post_install_message
|
|
25
|
+
say "\n🔐 API key authentication index migration created.", :green
|
|
26
|
+
say "Run `rails db:migrate` before deploying bcrypt authentication changes."
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def migration_version
|
|
32
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddAuthenticationIndexToApiKeys < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
INDEX_NAME = "index_api_keys_authentication_lookup"
|
|
5
|
+
COLUMNS = %i[prefix last4 digest_algorithm].freeze
|
|
6
|
+
|
|
7
|
+
disable_ddl_transaction!
|
|
8
|
+
|
|
9
|
+
def up
|
|
10
|
+
return if index_exists?(:api_keys, COLUMNS, name: INDEX_NAME)
|
|
11
|
+
|
|
12
|
+
options = { name: INDEX_NAME }
|
|
13
|
+
options[:algorithm] = :concurrently if connection.adapter_name.downcase.include?("postgres")
|
|
14
|
+
add_index :api_keys, COLUMNS, **options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def down
|
|
18
|
+
return unless index_exists?(:api_keys, COLUMNS, name: INDEX_NAME)
|
|
19
|
+
|
|
20
|
+
options = { name: INDEX_NAME }
|
|
21
|
+
options[:algorithm] = :concurrently if connection.adapter_name.downcase.include?("postgres")
|
|
22
|
+
remove_index :api_keys, **options
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -55,12 +55,11 @@ class CreateApiKeysTable < ActiveRecord::Migration<%= migration_version %>
|
|
|
55
55
|
|
|
56
56
|
# Index to optimize prefix-based lookups (especially for bcrypt)
|
|
57
57
|
t.index [:prefix, :digest_algorithm], name: "index_api_keys_on_prefix_and_digest_algorithm"
|
|
58
|
+
t.index [:prefix, :last4, :digest_algorithm], name: "index_api_keys_authentication_lookup"
|
|
58
59
|
|
|
59
60
|
# Optional indexes
|
|
60
61
|
t.index :prefix
|
|
61
62
|
t.index :last4 # Index potentially useful for UI lookups/filtering by masked token
|
|
62
|
-
t.index :owner_id if foreign_key_type # Index owner_id only if it's a separate column
|
|
63
|
-
t.index :owner_type if foreign_key_type # Index owner_type only if it's a separate column
|
|
64
63
|
t.index :expires_at
|
|
65
64
|
t.index :revoked_at
|
|
66
65
|
t.index :last_used_at
|
|
@@ -95,15 +94,4 @@ class CreateApiKeysTable < ActiveRecord::Migration<%= migration_version %>
|
|
|
95
94
|
# Fallback during initial setup or if connection isn't available
|
|
96
95
|
:text
|
|
97
96
|
end
|
|
98
|
-
|
|
99
|
-
# Provides the appropriate migration version syntax for the current Rails version.
|
|
100
|
-
def migration_version
|
|
101
|
-
major = ActiveRecord::VERSION::MAJOR
|
|
102
|
-
minor = ActiveRecord::VERSION::MINOR
|
|
103
|
-
if major >= 5
|
|
104
|
-
"[#{major}.#{minor}]"
|
|
105
|
-
else
|
|
106
|
-
""
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
97
|
+
end
|
|
@@ -23,6 +23,12 @@ ApiKeys.configure do |config|
|
|
|
23
23
|
# Configure how the dashboard integrates with your application's auth system.
|
|
24
24
|
# ============================================================================
|
|
25
25
|
|
|
26
|
+
# Controller class that the engine dashboard inherits from. Configure this in
|
|
27
|
+
# the initializer before engine controllers load. Accepts a Class or constant
|
|
28
|
+
# name string.
|
|
29
|
+
# Default: "::ApplicationController"
|
|
30
|
+
# config.parent_controller = "Admin::ApplicationController"
|
|
31
|
+
|
|
26
32
|
# The method that returns the current API key owner in your controllers.
|
|
27
33
|
# This should return the model instance that has_api_keys.
|
|
28
34
|
# Default: :current_user (works with Devise out of the box)
|
|
@@ -52,6 +58,24 @@ ApiKeys.configure do |config|
|
|
|
52
58
|
# Default: "‹ Home"
|
|
53
59
|
# config.return_text = "‹ Back to Settings"
|
|
54
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
|
+
|
|
55
79
|
# ============================================================================
|
|
56
80
|
# TOKEN PREFIXES
|
|
57
81
|
# ============================================================================
|
|
@@ -84,7 +108,11 @@ ApiKeys.configure do |config|
|
|
|
84
108
|
# When key_types IS configured and you specify a key_type, this setting
|
|
85
109
|
# is IGNORED - the prefix comes from the key type's configuration instead.
|
|
86
110
|
#
|
|
87
|
-
#
|
|
111
|
+
# Changing this later is SAFE for existing keys: every key stores its own
|
|
112
|
+
# prefix, so authentication keeps finding keys minted under retired
|
|
113
|
+
# prefixes (sha256 looks up by pure token digest; bcrypt uses a bounded
|
|
114
|
+
# stored-prefix lookup with an indexed last-four fallback for deployments
|
|
115
|
+
# with many retired prefixes). Only NEW keys wear the new prefix.
|
|
88
116
|
# Default: -> { "ak_" }
|
|
89
117
|
# config.token_prefix = -> { "myapp_" }
|
|
90
118
|
|
|
@@ -113,7 +141,8 @@ ApiKeys.configure do |config|
|
|
|
113
141
|
# - limit: Max keys of this type per owner per environment (nil = unlimited)
|
|
114
142
|
# - public: If true AND revocable: false, stores plaintext token in metadata
|
|
115
143
|
# so it can be viewed again in the dashboard. Use ONLY for publishable
|
|
116
|
-
# keys designed to be embedded in distributed apps.
|
|
144
|
+
# keys designed to be embedded in distributed apps. Public types must
|
|
145
|
+
# use a finite, non-empty permissions array (never :all). (default: false)
|
|
117
146
|
# SECURITY: NEVER set public: true on secret keys!
|
|
118
147
|
#
|
|
119
148
|
# config.key_types = {
|
|
@@ -227,6 +256,7 @@ ApiKeys.configure do |config|
|
|
|
227
256
|
# - Includes salt, computationally expensive by design
|
|
228
257
|
# - 10x-50x slower than SHA256 - impacts every API request
|
|
229
258
|
# - Better for low-entropy secrets (passwords), overkill for random API keys
|
|
259
|
+
# - Prefix + encoded random token must fit bcrypt's 72-byte input limit
|
|
230
260
|
#
|
|
231
261
|
# Default: :sha256
|
|
232
262
|
# config.hash_strategy = :sha256
|
|
@@ -240,10 +270,10 @@ ApiKeys.configure do |config|
|
|
|
240
270
|
# Default: true
|
|
241
271
|
# config.https_only_production = true
|
|
242
272
|
|
|
243
|
-
#
|
|
273
|
+
# Reject authentication (instead of just warning) over HTTP in production.
|
|
244
274
|
# Only applies when https_only_production is true.
|
|
245
|
-
# Default:
|
|
246
|
-
# config.https_strict_mode =
|
|
275
|
+
# Default: true
|
|
276
|
+
# config.https_strict_mode = true
|
|
247
277
|
|
|
248
278
|
# ============================================================================
|
|
249
279
|
# BACKGROUND JOBS & CALLBACKS
|
|
@@ -263,34 +293,41 @@ ApiKeys.configure do |config|
|
|
|
263
293
|
# config.enable_async_operations = true
|
|
264
294
|
|
|
265
295
|
# Track request counts (increments requests_count on each authentication).
|
|
266
|
-
#
|
|
296
|
+
# Exact counting bypasses last-used debouncing and therefore enqueues one stats
|
|
297
|
+
# job per successful request. High-frequency updates can impact queue/database
|
|
298
|
+
# performance.
|
|
267
299
|
# Default: false
|
|
268
300
|
# config.track_requests_count = false
|
|
269
301
|
|
|
302
|
+
# Minimum interval between last_used_at jobs when exact request counting is
|
|
303
|
+
# disabled. Set to 0 or nil to update on every successful authentication.
|
|
304
|
+
# Default: 1.minute
|
|
305
|
+
# config.stats_update_interval = 1.minute
|
|
306
|
+
|
|
270
307
|
# Queue names for background jobs.
|
|
271
308
|
# Default: :default
|
|
272
309
|
# config.stats_job_queue = :default
|
|
273
310
|
# config.callbacks_job_queue = :default
|
|
274
311
|
|
|
275
|
-
# Callbacks
|
|
276
|
-
#
|
|
277
|
-
#
|
|
312
|
+
# Callbacks enqueued on authentication attempts. Contexts are serializable
|
|
313
|
+
# hashes and never contain the token, request, result, or ApiKey object.
|
|
314
|
+
# before_authentication: { request_uuid: String }
|
|
315
|
+
# after_authentication: { success:, error_code:, api_key_id:,
|
|
316
|
+
# required_scope_check: (optional) }
|
|
278
317
|
#
|
|
279
318
|
# Default: empty procs (no-op)
|
|
280
|
-
# config.before_authentication = ->(
|
|
281
|
-
# config.after_authentication = ->(
|
|
319
|
+
# config.before_authentication = ->(context) { Rails.logger.info "Auth request: #{context[:request_uuid]}" }
|
|
320
|
+
# config.after_authentication = ->(context) { Analytics.track_auth(context) }
|
|
282
321
|
|
|
283
322
|
# ============================================================================
|
|
284
323
|
# PERFORMANCE
|
|
285
324
|
# ============================================================================
|
|
286
325
|
|
|
287
|
-
# Time-to-live (TTL) for caching API key
|
|
288
|
-
#
|
|
289
|
-
# Higher values = better performance (fewer DB queries)
|
|
290
|
-
# Lower values = faster propagation of revocations/changes
|
|
326
|
+
# Time-to-live (TTL) for caching API key ID lookup hints in Rails.cache.
|
|
291
327
|
#
|
|
292
|
-
#
|
|
293
|
-
#
|
|
328
|
+
# Cache entries are never authorization authority. Every hit reloads the
|
|
329
|
+
# current database row and cryptographically re-verifies the token, so
|
|
330
|
+
# revocation, expiration, and permission changes remain immediate.
|
|
294
331
|
#
|
|
295
332
|
# Set to 0 or nil to disable caching entirely.
|
|
296
333
|
# Default: 5.seconds
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: api_keys
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- rameerez
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rails
|
|
@@ -41,16 +41,22 @@ dependencies:
|
|
|
41
41
|
name: bcrypt
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 3.1.22
|
|
47
|
+
- - "<"
|
|
45
48
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
49
|
+
version: '4'
|
|
47
50
|
type: :runtime
|
|
48
51
|
prerelease: false
|
|
49
52
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
53
|
requirements:
|
|
51
|
-
- - "
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: 3.1.22
|
|
57
|
+
- - "<"
|
|
52
58
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
59
|
+
version: '4'
|
|
54
60
|
description: Add secure, production-ready API key authentication to your Rails app
|
|
55
61
|
in minutes. Handles key generation, hashing, expiration, revocation, per-key scopes;
|
|
56
62
|
plus a drop-in dashboard for your users to self-issue and manage their own API keys.
|
|
@@ -60,14 +66,10 @@ executables: []
|
|
|
60
66
|
extensions: []
|
|
61
67
|
extra_rdoc_files: []
|
|
62
68
|
files:
|
|
63
|
-
- ".simplecov"
|
|
64
|
-
- AGENTS.md
|
|
65
|
-
- Appraisals
|
|
66
69
|
- CHANGELOG.md
|
|
67
|
-
- CLAUDE.md
|
|
68
70
|
- LICENSE.txt
|
|
69
71
|
- README.md
|
|
70
|
-
-
|
|
72
|
+
- SECURITY.md
|
|
71
73
|
- api_keys_dashboard.webp
|
|
72
74
|
- api_keys_permissions.webp
|
|
73
75
|
- api_keys_token.webp
|
|
@@ -92,10 +94,6 @@ files:
|
|
|
92
94
|
- app/views/api_keys/security/best_practices.html.erb
|
|
93
95
|
- app/views/layouts/api_keys/application.html.erb
|
|
94
96
|
- config/routes.rb
|
|
95
|
-
- context7.json
|
|
96
|
-
- gemfiles/rails_7.2.gemfile
|
|
97
|
-
- gemfiles/rails_8.0.gemfile
|
|
98
|
-
- gemfiles/rails_8.1.gemfile
|
|
99
97
|
- lib/api_keys.rb
|
|
100
98
|
- lib/api_keys/authentication.rb
|
|
101
99
|
- lib/api_keys/configuration.rb
|
|
@@ -116,8 +114,10 @@ files:
|
|
|
116
114
|
- lib/api_keys/services/token_generator.rb
|
|
117
115
|
- lib/api_keys/tenant_resolution.rb
|
|
118
116
|
- lib/api_keys/version.rb
|
|
117
|
+
- lib/generators/api_keys/add_authentication_index_generator.rb
|
|
119
118
|
- lib/generators/api_keys/add_key_types_generator.rb
|
|
120
119
|
- lib/generators/api_keys/install_generator.rb
|
|
120
|
+
- lib/generators/api_keys/templates/add_authentication_index_to_api_keys.rb.erb
|
|
121
121
|
- lib/generators/api_keys/templates/add_key_types_to_api_keys.rb.erb
|
|
122
122
|
- lib/generators/api_keys/templates/create_api_keys_table.rb.erb
|
|
123
123
|
- lib/generators/api_keys/templates/initializer.rb
|
|
@@ -144,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
144
144
|
- !ruby/object:Gem::Version
|
|
145
145
|
version: '0'
|
|
146
146
|
requirements: []
|
|
147
|
-
rubygems_version: 3.6.
|
|
147
|
+
rubygems_version: 3.6.9
|
|
148
148
|
specification_version: 4
|
|
149
149
|
summary: Gate your Rails API with secure, self-serve API keys in minutes
|
|
150
150
|
test_files: []
|
data/.simplecov
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# SimpleCov configuration file (auto-loaded before test suite)
|
|
4
|
-
# This keeps test_helper.rb clean and follows best practices
|
|
5
|
-
|
|
6
|
-
SimpleCov.start do
|
|
7
|
-
# Use SimpleFormatter for terminal-only output (no HTML generation)
|
|
8
|
-
formatter SimpleCov::Formatter::SimpleFormatter
|
|
9
|
-
|
|
10
|
-
# Track coverage for the lib directory (gem source code)
|
|
11
|
-
add_filter "/test/"
|
|
12
|
-
|
|
13
|
-
# Track the lib and app directories
|
|
14
|
-
track_files "{lib,app}/**/*.rb"
|
|
15
|
-
|
|
16
|
-
# Enable branch coverage for more detailed metrics
|
|
17
|
-
enable_coverage :branch
|
|
18
|
-
|
|
19
|
-
# Set minimum coverage threshold to prevent coverage regression
|
|
20
|
-
# Starting at 60% - can be increased as coverage improves
|
|
21
|
-
minimum_coverage line: 60, branch: 60
|
|
22
|
-
|
|
23
|
-
# Disambiguate parallel test runs
|
|
24
|
-
command_name "Job #{ENV['TEST_ENV_NUMBER']}" if ENV['TEST_ENV_NUMBER']
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Print coverage summary to terminal after tests complete
|
|
28
|
-
SimpleCov.at_exit do
|
|
29
|
-
SimpleCov.result.format!
|
|
30
|
-
puts "\n" + "=" * 60
|
|
31
|
-
puts "COVERAGE SUMMARY"
|
|
32
|
-
puts "=" * 60
|
|
33
|
-
puts "Line Coverage: #{SimpleCov.result.covered_percent.round(2)}%"
|
|
34
|
-
puts "Branch Coverage: #{SimpleCov.result.coverage_statistics[:branch]&.percent&.round(2) || 'N/A'}%"
|
|
35
|
-
puts "=" * 60
|
|
36
|
-
end
|
data/AGENTS.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to AI Agents (like OpenAI's Codex, Cursor Agent, Claude Code, etc) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
|
data/Appraisals
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Test against multiple Rails versions
|
|
4
|
-
# This is our most critical dependency point
|
|
5
|
-
|
|
6
|
-
appraise "rails-7.2" do
|
|
7
|
-
gem "rails", "~> 7.2.3"
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
appraise "rails-8.0" do
|
|
11
|
-
gem "rails", "~> 8.0"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Test against Rails 8.1 (latest)
|
|
15
|
-
appraise "rails-8.1" do
|
|
16
|
-
gem "rails", "~> 8.1.2"
|
|
17
|
-
end
|
data/CLAUDE.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
|
data/Rakefile
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
begin
|
|
2
|
-
require "bundler/setup"
|
|
3
|
-
rescue LoadError
|
|
4
|
-
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
require "bundler/gem_tasks"
|
|
8
|
-
|
|
9
|
-
require "rdoc/task"
|
|
10
|
-
|
|
11
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
|
12
|
-
rdoc.rdoc_dir = "rdoc"
|
|
13
|
-
rdoc.title = "Pay"
|
|
14
|
-
rdoc.options << "--line-numbers"
|
|
15
|
-
rdoc.rdoc_files.include("README.md")
|
|
16
|
-
rdoc.rdoc_files.include("lib/**/*.rb")
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Removed loading of engine tasks to avoid pulling in dummy app/vendor tests
|
|
20
|
-
# APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
|
21
|
-
# load "rails/tasks/engine.rake"
|
|
22
|
-
|
|
23
|
-
# Removed Rails statistics task; not needed for gem tests
|
|
24
|
-
# load "rails/tasks/statistics.rake"
|
|
25
|
-
|
|
26
|
-
require "rake/testtask"
|
|
27
|
-
|
|
28
|
-
# Ensure any test task created by engine.rake is cleared so only this suite runs
|
|
29
|
-
Rake::Task[:test].clear if Rake::Task.task_defined?(:test)
|
|
30
|
-
|
|
31
|
-
Rake::TestTask.new(:test) do |t|
|
|
32
|
-
t.libs << "test"
|
|
33
|
-
t.verbose = false
|
|
34
|
-
t.test_files = FileList['test/**/*_test.rb'].exclude('test/dummy/**/*')
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
task default: :test
|
data/context7.json
DELETED
data/gemfiles/rails_7.2.gemfile
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# This file was generated by Appraisal
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
gem "rake", "~> 13.0"
|
|
6
|
-
gem "rails", "~> 7.2.3"
|
|
7
|
-
|
|
8
|
-
group :development, :test do
|
|
9
|
-
gem "appraisal"
|
|
10
|
-
gem "minitest", "~> 6.0"
|
|
11
|
-
gem "minitest-mock"
|
|
12
|
-
gem "minitest-reporters"
|
|
13
|
-
gem "rack-test"
|
|
14
|
-
gem "simplecov", require: false
|
|
15
|
-
gem "sqlite3", ">= 2.1"
|
|
16
|
-
gem "bootsnap", require: false
|
|
17
|
-
gem "mocha", "~> 2.0"
|
|
18
|
-
gem "ostruct"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
gemspec path: "../"
|
data/gemfiles/rails_8.0.gemfile
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# This file was generated by Appraisal
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
gem "rake", "~> 13.0"
|
|
6
|
-
gem "rails", "~> 8.0"
|
|
7
|
-
|
|
8
|
-
group :development, :test do
|
|
9
|
-
gem "appraisal"
|
|
10
|
-
gem "minitest", "~> 6.0"
|
|
11
|
-
gem "minitest-mock"
|
|
12
|
-
gem "minitest-reporters"
|
|
13
|
-
gem "rack-test"
|
|
14
|
-
gem "simplecov", require: false
|
|
15
|
-
gem "sqlite3", ">= 2.1"
|
|
16
|
-
gem "bootsnap", require: false
|
|
17
|
-
gem "mocha", "~> 2.0"
|
|
18
|
-
gem "ostruct"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
gemspec path: "../"
|
data/gemfiles/rails_8.1.gemfile
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# This file was generated by Appraisal
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
gem "rake", "~> 13.0"
|
|
6
|
-
gem "rails", "~> 8.1.2"
|
|
7
|
-
|
|
8
|
-
group :development, :test do
|
|
9
|
-
gem "appraisal"
|
|
10
|
-
gem "minitest", "~> 6.0"
|
|
11
|
-
gem "minitest-mock"
|
|
12
|
-
gem "minitest-reporters"
|
|
13
|
-
gem "rack-test"
|
|
14
|
-
gem "simplecov", require: false
|
|
15
|
-
gem "sqlite3", ">= 2.1"
|
|
16
|
-
gem "bootsnap", require: false
|
|
17
|
-
gem "mocha", "~> 2.0"
|
|
18
|
-
gem "ostruct"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
gemspec path: "../"
|