api_keys 0.3.0 → 0.4.0
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 +51 -0
- data/README.md +101 -39
- data/SECURITY.md +33 -0
- data/app/controllers/api_keys/application_controller.rb +58 -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 +374 -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 +32 -0
- data/lib/generators/api_keys/templates/create_api_keys_table.rb.erb +2 -3
- data/lib/generators/api_keys/templates/initializer.rb +36 -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,32 @@
|
|
|
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
|
+
|
|
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
|
+
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
|
|
@@ -106,4 +105,4 @@ class CreateApiKeysTable < ActiveRecord::Migration<%= migration_version %>
|
|
|
106
105
|
""
|
|
107
106
|
end
|
|
108
107
|
end
|
|
109
|
-
end
|
|
108
|
+
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)
|
|
@@ -84,7 +90,11 @@ ApiKeys.configure do |config|
|
|
|
84
90
|
# When key_types IS configured and you specify a key_type, this setting
|
|
85
91
|
# is IGNORED - the prefix comes from the key type's configuration instead.
|
|
86
92
|
#
|
|
87
|
-
#
|
|
93
|
+
# Changing this later is SAFE for existing keys: every key stores its own
|
|
94
|
+
# prefix, so authentication keeps finding keys minted under retired
|
|
95
|
+
# prefixes (sha256 looks up by pure token digest; bcrypt uses a bounded
|
|
96
|
+
# stored-prefix lookup with an indexed last-four fallback for deployments
|
|
97
|
+
# with many retired prefixes). Only NEW keys wear the new prefix.
|
|
88
98
|
# Default: -> { "ak_" }
|
|
89
99
|
# config.token_prefix = -> { "myapp_" }
|
|
90
100
|
|
|
@@ -113,7 +123,8 @@ ApiKeys.configure do |config|
|
|
|
113
123
|
# - limit: Max keys of this type per owner per environment (nil = unlimited)
|
|
114
124
|
# - public: If true AND revocable: false, stores plaintext token in metadata
|
|
115
125
|
# so it can be viewed again in the dashboard. Use ONLY for publishable
|
|
116
|
-
# keys designed to be embedded in distributed apps.
|
|
126
|
+
# keys designed to be embedded in distributed apps. Public types must
|
|
127
|
+
# use a finite, non-empty permissions array (never :all). (default: false)
|
|
117
128
|
# SECURITY: NEVER set public: true on secret keys!
|
|
118
129
|
#
|
|
119
130
|
# config.key_types = {
|
|
@@ -227,6 +238,7 @@ ApiKeys.configure do |config|
|
|
|
227
238
|
# - Includes salt, computationally expensive by design
|
|
228
239
|
# - 10x-50x slower than SHA256 - impacts every API request
|
|
229
240
|
# - Better for low-entropy secrets (passwords), overkill for random API keys
|
|
241
|
+
# - Prefix + encoded random token must fit bcrypt's 72-byte input limit
|
|
230
242
|
#
|
|
231
243
|
# Default: :sha256
|
|
232
244
|
# config.hash_strategy = :sha256
|
|
@@ -240,10 +252,10 @@ ApiKeys.configure do |config|
|
|
|
240
252
|
# Default: true
|
|
241
253
|
# config.https_only_production = true
|
|
242
254
|
|
|
243
|
-
#
|
|
255
|
+
# Reject authentication (instead of just warning) over HTTP in production.
|
|
244
256
|
# Only applies when https_only_production is true.
|
|
245
|
-
# Default:
|
|
246
|
-
# config.https_strict_mode =
|
|
257
|
+
# Default: true
|
|
258
|
+
# config.https_strict_mode = true
|
|
247
259
|
|
|
248
260
|
# ============================================================================
|
|
249
261
|
# BACKGROUND JOBS & CALLBACKS
|
|
@@ -263,34 +275,41 @@ ApiKeys.configure do |config|
|
|
|
263
275
|
# config.enable_async_operations = true
|
|
264
276
|
|
|
265
277
|
# Track request counts (increments requests_count on each authentication).
|
|
266
|
-
#
|
|
278
|
+
# Exact counting bypasses last-used debouncing and therefore enqueues one stats
|
|
279
|
+
# job per successful request. High-frequency updates can impact queue/database
|
|
280
|
+
# performance.
|
|
267
281
|
# Default: false
|
|
268
282
|
# config.track_requests_count = false
|
|
269
283
|
|
|
284
|
+
# Minimum interval between last_used_at jobs when exact request counting is
|
|
285
|
+
# disabled. Set to 0 or nil to update on every successful authentication.
|
|
286
|
+
# Default: 1.minute
|
|
287
|
+
# config.stats_update_interval = 1.minute
|
|
288
|
+
|
|
270
289
|
# Queue names for background jobs.
|
|
271
290
|
# Default: :default
|
|
272
291
|
# config.stats_job_queue = :default
|
|
273
292
|
# config.callbacks_job_queue = :default
|
|
274
293
|
|
|
275
|
-
# Callbacks
|
|
276
|
-
#
|
|
277
|
-
#
|
|
294
|
+
# Callbacks enqueued on authentication attempts. Contexts are serializable
|
|
295
|
+
# hashes and never contain the token, request, result, or ApiKey object.
|
|
296
|
+
# before_authentication: { request_uuid: String }
|
|
297
|
+
# after_authentication: { success:, error_code:, api_key_id:,
|
|
298
|
+
# required_scope_check: (optional) }
|
|
278
299
|
#
|
|
279
300
|
# Default: empty procs (no-op)
|
|
280
|
-
# config.before_authentication = ->(
|
|
281
|
-
# config.after_authentication = ->(
|
|
301
|
+
# config.before_authentication = ->(context) { Rails.logger.info "Auth request: #{context[:request_uuid]}" }
|
|
302
|
+
# config.after_authentication = ->(context) { Analytics.track_auth(context) }
|
|
282
303
|
|
|
283
304
|
# ============================================================================
|
|
284
305
|
# PERFORMANCE
|
|
285
306
|
# ============================================================================
|
|
286
307
|
|
|
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
|
|
308
|
+
# Time-to-live (TTL) for caching API key ID lookup hints in Rails.cache.
|
|
291
309
|
#
|
|
292
|
-
#
|
|
293
|
-
#
|
|
310
|
+
# Cache entries are never authorization authority. Every hit reloads the
|
|
311
|
+
# current database row and cryptographically re-verifies the token, so
|
|
312
|
+
# revocation, expiration, and permission changes remain immediate.
|
|
294
313
|
#
|
|
295
314
|
# Set to 0 or nil to disable caching entirely.
|
|
296
315
|
# 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.0
|
|
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: "../"
|