action_ip_filter 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d061bab952784ba92d038b0a28d34418e9c360d6d6abd543621535170a9a21c
4
- data.tar.gz: 8a57d44aa950fb7c9bc0dad8a3493add8c626df62cecea9d0a8536d5ae7bc6d6
3
+ metadata.gz: af3d4fbb598b8802bc90a29a75a0f6f3d38cd77757a04c8bef538fe34172ba14
4
+ data.tar.gz: 13e2e5ed9b621b70c27f11046149643d7237fe977766e29722f9f293c5ebe46d
5
5
  SHA512:
6
- metadata.gz: 7f868950a961ac26cfebace46005f363f4cace350a63d0c58996b20966312a245794b8fe571b0abec32c216baae81e41fad39ea0eb393997348043e650263c3e
7
- data.tar.gz: 746dc0312e87366a4d6d4e36e0bd31648255d646fb6f7d103f6e00bb4d68d1863ef4c2154737db74f3655934da81a73737b2b6a920d3338a0e8c9b69faf09afd
6
+ metadata.gz: 578ca535fedf30d89e7681b286f2d68518cef4a001802d053cd49cc1a42cf9e9f85d7628230b4a28ddf7c54cc6225a158ce4f7f161d8f09c0fb55833adafb154
7
+ data.tar.gz: 1ba8977aa885e48794147c6c09c1d4c4bbd71f35974371cfb5ef8c75ea47efbf179480fea9f1c6259ea57ac7fdb0fc58e562969a230815bec857aa109da97551
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2025-11-29
4
+
5
+ ### Breaking Changes
6
+
7
+ #### Change `ip_resolver` to use controller context instead of request parameter
8
+
9
+ Usage has changed. The `request` parameter in the Proc was previously required, but it is no longer needed. You can now access controller methods (`request`, `params`, etc.) directly instead of receiving `request` as an argument.
10
+
11
+ Before:
12
+
13
+ ```
14
+ config.ip_resolver = ->(request) {
15
+ request.headers["X-Forwarded-For"]&.split(",")&.first&.strip || request.remote_ip
16
+ }
17
+ ```
18
+
19
+ After:
20
+
21
+ ```
22
+ config.ip_resolver = -> {
23
+ request.headers["X-Forwarded-For"]&.split(",")&.first&.strip || request.remote_ip
24
+ }
25
+ ```
26
+
3
27
  ## [0.2.0] - 2025-11-28
4
28
 
5
29
  ### Breaking Changes
data/README.md CHANGED
@@ -18,6 +18,24 @@ Unlike Rack middleware solutions (e.g., `rack-attack`), action_ip_filter operate
18
18
  - Minimal overhead (no processing for unrestricted endpoints)
19
19
  - Simple, declarative configuration per controller
20
20
 
21
+ ### Why not Rails native `rate_limit`?
22
+
23
+ Rails 8.0 introduced `rate_limit`, which could be adapted for IP filtering:
24
+
25
+ ```ruby
26
+ # a slightly tricky approach involving rate_limit
27
+ rate_limit to: 0, within: 0.second, only: :create, unless: -> {
28
+ allowed_ips.include?(request.remote_ip)
29
+ }
30
+ ```
31
+
32
+ However, this approach has drawbacks:
33
+ - Semantic mismatch: `rate_limit` is designed for rate limiting, not access control
34
+ - Requires cache store: `rate_limit` needs a cache backend for counting, unnecessary for simple IP allowlists
35
+ - Inverted logic: You're "rate limiting everyone except allowed IPs" rather than "allowing specific IPs"
36
+
37
+ action_ip_filter provides a purpose-built, declarative API for IP-based access control.
38
+
21
39
  ## Installation
22
40
 
23
41
  Add to your Gemfile:
@@ -114,7 +132,7 @@ Configure global settings in an initializer:
114
132
  # config/initializers/action_ip_filter.rb
115
133
  ActionIpFilter.configure do |config|
116
134
  # Custom IP resolver (default: request.remote_ip)
117
- config.ip_resolver = ->(request) {
135
+ config.ip_resolver = -> {
118
136
  request.headers["X-Forwarded-For"]&.split(",")&.first&.strip || request.remote_ip
119
137
  }
120
138
 
@@ -133,7 +151,7 @@ end
133
151
 
134
152
  | Option | Default | Description |
135
153
  |--------|-------------------------------------|----------------------------------------------------|
136
- | `ip_resolver` | `->(request) { request.remote_ip }` | Proc that extracts client IP from request |
154
+ | `ip_resolver` | `-> { request.remote_ip }` | Proc that extracts client IP from request |
137
155
  | `on_denied` | `-> { head :forbidden }` | Handler called when access is denied (returns 403) |
138
156
  | `logger` | `Rails.logger` | Logger instance for denied request logging |
139
157
  | `log_denials` | `true` | Whether to log denied requests as warn level |
@@ -2,19 +2,19 @@
2
2
 
3
3
  module ActionIpFilter
4
4
  class Configuration
5
- # @rbs @ip_resolver: ^(ActionDispatch::Request) -> String?
5
+ # @rbs @ip_resolver: ^() -> String?
6
6
  # @rbs @on_denied: ^() -> void
7
7
  # @rbs @logger: Logger?
8
8
  # @rbs @log_denials: bool
9
9
 
10
- attr_accessor :ip_resolver #: ^(ActionDispatch::Request) -> String?
10
+ attr_accessor :ip_resolver #: ^() -> String?
11
11
  attr_accessor :on_denied #: ^() -> void
12
12
  attr_accessor :logger #: Logger?
13
13
  attr_accessor :log_denials #: bool
14
14
 
15
15
  # @rbs return: void
16
16
  def initialize
17
- @ip_resolver = ->(request) { request.remote_ip }
17
+ @ip_resolver = -> { request.remote_ip } # steep:ignore NoMethod
18
18
  @on_denied = -> { head :forbidden } # steep:ignore NoMethod
19
19
  @logger = nil
20
20
  @log_denials = true
@@ -62,7 +62,7 @@ module ActionIpFilter
62
62
  def verify_ip_access(restriction)
63
63
  return if restriction.nil? || ActionIpFilter.test_mode?
64
64
 
65
- client_ip = ActionIpFilter.configuration.ip_resolver.call(request)
65
+ client_ip = instance_exec(&ActionIpFilter.configuration.ip_resolver) #: String?
66
66
  allowed_ips = resolve_allowed_ips(restriction[:allowed_ips])
67
67
 
68
68
  unless IpMatcher.allowed?(client_ip, allowed_ips)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionIpFilter
4
- VERSION = "0.2.0" #: String
4
+ VERSION = "0.3.0" #: String
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_ip_filter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SmartBank, Inc.