action_ip_filter 0.2.0 → 0.3.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 +28 -0
- data/README.md +21 -3
- data/lib/action_ip_filter/configuration.rb +3 -3
- data/lib/action_ip_filter/ip_filterable.rb +1 -1
- data/lib/action_ip_filter/ip_matcher.rb +2 -7
- data/lib/action_ip_filter/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a00718ef1f7bccb16f52d1b8a1e5f9e746e9ba15f933795be419ff3c0d303c3d
|
|
4
|
+
data.tar.gz: 5c0f2e216e5481ec40f6156dd512d6a5ea5af39de88d64e7c06504faa5966fd7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed618b5060effa6e112c7cfce1616f5f43467728971503779caff3230ec1b3506c18585766d41df45088e45c74e1b3756522341e5ca873b96d0f73284e15ee4b
|
|
7
|
+
data.tar.gz: 355e3c64444d092f466166c11c2d9afad5dfedab274e4a80f56e47d6d724a64b63dc21774e781496889d07e435c675d1b528bd017e9c79ef7f568fb59e9a49e0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.1] - 2025-12-01
|
|
4
|
+
|
|
5
|
+
- Simplify IpMatcher#match? by removing unnecessary branching [#3](https://github.com/smartbank-inc/action_ip_filter/pull/3)
|
|
6
|
+
|
|
7
|
+
## [0.3.0] - 2025-11-29
|
|
8
|
+
|
|
9
|
+
### Breaking Changes
|
|
10
|
+
|
|
11
|
+
#### Change `ip_resolver` to use controller context instead of request parameter [#1](https://github.com/smartbank-inc/action_ip_filter/pull/1)
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
Before:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
config.ip_resolver = ->(request) {
|
|
19
|
+
request.headers["X-Forwarded-For"]&.split(",")&.first&.strip || request.remote_ip
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
After:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
config.ip_resolver = -> {
|
|
27
|
+
request.headers["X-Forwarded-For"]&.split(",")&.first&.strip || request.remote_ip
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
3
31
|
## [0.2.0] - 2025-11-28
|
|
4
32
|
|
|
5
33
|
### Breaking Changes
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# action_ip_filter
|
|
1
|
+
# action_ip_filter [](https://github.com/smartbank-inc/action_ip_filter/actions/workflows/ci.yml) [](https://rubygems.org/gems/action_ip_filter)
|
|
2
2
|
|
|
3
3
|
A lightweight gem that provides IP address restrictions for Rails controllers at the action level.
|
|
4
4
|
|
|
@@ -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 = ->
|
|
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` | `->
|
|
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: ^(
|
|
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 #: ^(
|
|
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 = ->
|
|
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
|
|
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)
|
|
@@ -33,13 +33,8 @@ module ActionIpFilter
|
|
|
33
33
|
# @rbs allowed_ip: String
|
|
34
34
|
# @rbs return: bool
|
|
35
35
|
def match?(client_addr, allowed_ip)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
range.include?(client_addr)
|
|
39
|
-
else
|
|
40
|
-
allowed_addr = IPAddr.new(allowed_ip)
|
|
41
|
-
client_addr == allowed_addr
|
|
42
|
-
end
|
|
36
|
+
allowed_addr = IPAddr.new(allowed_ip)
|
|
37
|
+
allowed_addr.include?(client_addr)
|
|
43
38
|
rescue IPAddr::InvalidAddressError
|
|
44
39
|
false
|
|
45
40
|
end
|