action_ip_filter 0.1.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 +7 -0
- data/CHANGELOG.md +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +216 -0
- data/lib/action_ip_filter/configuration.rb +42 -0
- data/lib/action_ip_filter/ip_filterable.rb +99 -0
- data/lib/action_ip_filter/ip_matcher.rb +48 -0
- data/lib/action_ip_filter/railtie.rb +11 -0
- data/lib/action_ip_filter/test_helpers.rb +33 -0
- data/lib/action_ip_filter/version.rb +5 -0
- data/lib/action_ip_filter.rb +29 -0
- metadata +153 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9a0156496085eb4b1b02b0c96bc4e16859dddc35d2cf9920c9f34ffba6f2f32c
|
|
4
|
+
data.tar.gz: 522544d5021bf15706737c4ecbd32dc783ccfee77bad855d752af7a7653f06e5
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0463f7af276564a3f6d2a1175cc86ad2d0a0236261015befc3f73ba992f29994b85a36d4c86237e8df9d031cffcb6ea50deda34cf597608a9fc20cd742c2a3d2
|
|
7
|
+
data.tar.gz: e89379d17ac2c97b1607046405ef2366502a71700c53daa06f9ed3fa7fe5a8ca4d81faee1c998f784705c97e20723fa47050baeb359d3cac11223a2dd33acd63
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 SmartBank, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# action_ip_filter
|
|
2
|
+
|
|
3
|
+
A lightweight gem that provides IP address restrictions for Rails controllers at the action level.
|
|
4
|
+
|
|
5
|
+
## Why action_ip_filter?
|
|
6
|
+
|
|
7
|
+
Unlike Rack middleware solutions (e.g., `rack-attack`), action_ip_filter operates at the controller level:
|
|
8
|
+
|
|
9
|
+
| Feature | rack-attack | action_ip_filter |
|
|
10
|
+
|---------|-------------|----------------|
|
|
11
|
+
| Layer | Rack middleware (all requests) | Controller before_action |
|
|
12
|
+
| Granularity | Path/IP based | Controller/Action based |
|
|
13
|
+
| Overhead | Every request evaluated | Only specified actions |
|
|
14
|
+
| Use case | DDoS protection, rate limiting | Admin panels, webhooks |
|
|
15
|
+
|
|
16
|
+
**Use this gem when you need:**
|
|
17
|
+
- IP restrictions on specific controller actions only
|
|
18
|
+
- Minimal overhead (no processing for unrestricted endpoints)
|
|
19
|
+
- Simple, declarative configuration per controller
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
Add to your Gemfile:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem "action_ip_filter"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then run:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bundle install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Basic Usage
|
|
38
|
+
|
|
39
|
+
Include the concern and use `restrict_ip` to protect specific actions:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
class AdminController < ApplicationController
|
|
43
|
+
include ActionIpFilter::IpFilterable
|
|
44
|
+
|
|
45
|
+
restrict_ip :index, :show, allowed_ips: %w[192.0.2.0/24 198.51.100.1]
|
|
46
|
+
|
|
47
|
+
def index
|
|
48
|
+
# Only accessible from 192.0.2.0/24 or 198.51.100.1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def show
|
|
52
|
+
# Also restricted
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def public_action
|
|
56
|
+
# Not restricted
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Restrict All Actions
|
|
62
|
+
|
|
63
|
+
Use `restrict_ip_for_all` to protect all actions with optional exceptions:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
class WebhooksController < ApplicationController
|
|
67
|
+
include ActionIpFilter::IpFilterable
|
|
68
|
+
|
|
69
|
+
restrict_ip_for_all allowed_ips: ENV["WEBHOOK_ALLOWED_IPS"].to_s.split(","),
|
|
70
|
+
except: [:health_check]
|
|
71
|
+
|
|
72
|
+
def stripe
|
|
73
|
+
# Restricted
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def health_check
|
|
77
|
+
# Not restricted
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Dynamic IP Lists
|
|
83
|
+
|
|
84
|
+
Pass a Proc for dynamic IP resolution:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
class SecureController < ApplicationController
|
|
88
|
+
include ActionIpFilter::IpFilterable
|
|
89
|
+
|
|
90
|
+
restrict_ip :sensitive_action,
|
|
91
|
+
allowed_ips: -> { Rails.application.credentials.dig(:allowed_ips) || [] }
|
|
92
|
+
end
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Custom Denial Handler
|
|
96
|
+
|
|
97
|
+
Customize the response when access is denied. The block is executed via `instance_exec` in the controller context, so you can use controller methods like `head`, `render`, etc. The request object is passed as an argument:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
class ApiController < ApplicationController
|
|
101
|
+
include ActionIpFilter::IpFilterable
|
|
102
|
+
|
|
103
|
+
restrict_ip :create,
|
|
104
|
+
allowed_ips: %w[192.0.2.0/24],
|
|
105
|
+
on_denied: -> { render json: { error: "Access denied from #{request.remote_ip}" }, status: :forbidden }
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Configuration
|
|
110
|
+
|
|
111
|
+
Configure global settings in an initializer:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
# config/initializers/action_ip_filter.rb
|
|
115
|
+
ActionIpFilter.configure do |config|
|
|
116
|
+
# Custom IP resolver (default: request.remote_ip)
|
|
117
|
+
config.ip_resolver = ->(request) {
|
|
118
|
+
request.headers["X-Forwarded-For"]&.split(",")&.first&.strip || request.remote_ip
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Default denial handler (receives request, executed via instance_exec in controller)
|
|
122
|
+
config.on_denied = -> { head :forbidden }
|
|
123
|
+
|
|
124
|
+
# Logger for denied requests
|
|
125
|
+
config.logger = Rails.logger
|
|
126
|
+
|
|
127
|
+
# Enable/disable denial logging (default: true)
|
|
128
|
+
config.log_denials = true
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Default Values
|
|
133
|
+
|
|
134
|
+
| Option | Default | Description |
|
|
135
|
+
|--------|-------------------------------------|----------------------------------------------------|
|
|
136
|
+
| `ip_resolver` | `->(request) { request.remote_ip }` | Proc that extracts client IP from request |
|
|
137
|
+
| `on_denied` | `-> { head :forbidden }` | Handler called when access is denied (returns 403) |
|
|
138
|
+
| `logger` | `Rails.logger` | Logger instance for denied request logging |
|
|
139
|
+
| `log_denials` | `true` | Whether to log denied requests as warn level |
|
|
140
|
+
|
|
141
|
+
## Testing
|
|
142
|
+
|
|
143
|
+
### Bypass IP Filter in Tests
|
|
144
|
+
|
|
145
|
+
Use the test helpers to bypass IP restrictions:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
# spec/rails_helper.rb
|
|
149
|
+
RSpec.configure do |config|
|
|
150
|
+
config.include ActionIpFilter::TestHelpers
|
|
151
|
+
|
|
152
|
+
# Option 1: Globally bypass in all tests
|
|
153
|
+
config.before do
|
|
154
|
+
ActionIpFilter.test_mode = true
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Or use helpers for specific tests:
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
RSpec.describe "Admin", type: :request do
|
|
163
|
+
include ActionIpFilter::TestHelpers
|
|
164
|
+
|
|
165
|
+
describe "GET /admin" do
|
|
166
|
+
it "allows access when filter is bypassed" do
|
|
167
|
+
without_ip_filter do
|
|
168
|
+
get "/admin"
|
|
169
|
+
expect(response).to have_http_status(:ok)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "denies access from unauthorized IP" do
|
|
174
|
+
with_ip_filter do
|
|
175
|
+
get "/admin"
|
|
176
|
+
expect(response).to have_http_status(:not_found)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Supported IP Formats
|
|
184
|
+
|
|
185
|
+
- Single IPv4: `192.0.2.1`
|
|
186
|
+
- Single IPv6: `::1`, `2001:db8::1`
|
|
187
|
+
- CIDR notation: `192.0.2.0/24`
|
|
188
|
+
- IPv6 CIDR: `2001:db8::/32`
|
|
189
|
+
|
|
190
|
+
## Logging
|
|
191
|
+
|
|
192
|
+
When `log_denials` is enabled (default), denied requests are logged:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
[ActionIpFilter] Access denied for IP: 192.0.2.1 on MyController#index
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Development
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# Install dependencies
|
|
202
|
+
bundle install
|
|
203
|
+
|
|
204
|
+
# Run tests
|
|
205
|
+
bundle exec rspec
|
|
206
|
+
|
|
207
|
+
# Run type checker
|
|
208
|
+
bundle exec rake rbs
|
|
209
|
+
|
|
210
|
+
# Run linter
|
|
211
|
+
bundle exec standardrb
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionIpFilter
|
|
4
|
+
class Configuration
|
|
5
|
+
# @rbs @ip_resolver: ^(ActionDispatch::Request) -> String?
|
|
6
|
+
# @rbs @on_denied: ^() -> void
|
|
7
|
+
# @rbs @logger: Logger?
|
|
8
|
+
# @rbs @log_denials: bool
|
|
9
|
+
|
|
10
|
+
attr_accessor :ip_resolver #: ^(ActionDispatch::Request) -> String?
|
|
11
|
+
attr_accessor :on_denied #: ^() -> void
|
|
12
|
+
attr_accessor :logger #: Logger?
|
|
13
|
+
attr_accessor :log_denials #: bool
|
|
14
|
+
|
|
15
|
+
# @rbs return: void
|
|
16
|
+
def initialize
|
|
17
|
+
@ip_resolver = ->(request) { request.remote_ip }
|
|
18
|
+
@on_denied = -> { head :forbidden } # steep:ignore NoMethod
|
|
19
|
+
@logger = nil
|
|
20
|
+
@log_denials = true
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
# @rbs @configuration: Configuration?
|
|
26
|
+
|
|
27
|
+
# @rbs return: Configuration
|
|
28
|
+
def configuration
|
|
29
|
+
@configuration ||= Configuration.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @rbs () { (Configuration) -> void } -> void
|
|
33
|
+
def configure
|
|
34
|
+
yield(configuration)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @rbs return: void
|
|
38
|
+
def reset_configuration!
|
|
39
|
+
@configuration = Configuration.new
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module ActionIpFilter
|
|
6
|
+
module IpFilterable
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
class_attribute :action_ip_restrictions, default: {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @rbs!
|
|
14
|
+
# type restriction_options = { allowed_ips: Array[String] | ^() -> Array[String], on_denied: (^() -> void)? }
|
|
15
|
+
# def action_ip_restrictions: () -> Hash[Symbol, untyped]
|
|
16
|
+
# def action_ip_restrictions=: (Hash[Symbol, untyped]) -> void
|
|
17
|
+
|
|
18
|
+
# @rbs!
|
|
19
|
+
# def request: () -> ActionDispatch::Request
|
|
20
|
+
# def action_name: () -> String
|
|
21
|
+
# def instance_exec: (*untyped) { () -> void } -> void
|
|
22
|
+
# def before_action: (*untyped, **untyped) -> void
|
|
23
|
+
|
|
24
|
+
class_methods do
|
|
25
|
+
# @rbs *actions: Symbol
|
|
26
|
+
# @rbs allowed_ips: Array[String] | ^() -> Array[String]
|
|
27
|
+
# @rbs on_denied: (^() -> void)?
|
|
28
|
+
# @rbs return: void
|
|
29
|
+
def restrict_ip(*actions, allowed_ips:, on_denied: nil)
|
|
30
|
+
actions.flatten.each do |action|
|
|
31
|
+
self.action_ip_restrictions = action_ip_restrictions.merge(action.to_sym => {allowed_ips:, on_denied:})
|
|
32
|
+
before_action -> { check_ip_restriction(action) }, only: action
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @rbs allowed_ips: Array[String] | ^() -> Array[String]
|
|
37
|
+
# @rbs except: Array[Symbol]
|
|
38
|
+
# @rbs on_denied: (^() -> void)?
|
|
39
|
+
# @rbs return: void
|
|
40
|
+
def restrict_ip_for_all(allowed_ips:, except: [], on_denied: nil)
|
|
41
|
+
# note: hyphen is not allowed in method (i.e., action) names, so it's safe to use it as a marker
|
|
42
|
+
self.action_ip_restrictions = action_ip_restrictions.merge("all-marker": {allowed_ips:, on_denied:})
|
|
43
|
+
before_action :check_ip_restriction_for_all, except:
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# @rbs action: Symbol
|
|
50
|
+
# @rbs return: void
|
|
51
|
+
def check_ip_restriction(action)
|
|
52
|
+
verify_ip_access(action_ip_restrictions[action.to_sym])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @rbs return: void
|
|
56
|
+
def check_ip_restriction_for_all
|
|
57
|
+
verify_ip_access(action_ip_restrictions[:"all-marker"])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @rbs restriction: restriction_options?
|
|
61
|
+
# @rbs return: void
|
|
62
|
+
def verify_ip_access(restriction)
|
|
63
|
+
return if restriction.nil? || ActionIpFilter.test_mode?
|
|
64
|
+
|
|
65
|
+
client_ip = ActionIpFilter.configuration.ip_resolver.call(request)
|
|
66
|
+
allowed_ips = resolve_allowed_ips(restriction[:allowed_ips])
|
|
67
|
+
|
|
68
|
+
unless IpMatcher.allowed?(client_ip, allowed_ips)
|
|
69
|
+
log_ip_denial(client_ip)
|
|
70
|
+
on_denied = restriction[:on_denied] || ActionIpFilter.configuration.on_denied
|
|
71
|
+
instance_exec(&on_denied)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @rbs allowed_ips: Array[String] | ^() -> Array[String]
|
|
76
|
+
# @rbs return: Array[String]
|
|
77
|
+
def resolve_allowed_ips(allowed_ips)
|
|
78
|
+
case allowed_ips
|
|
79
|
+
when Proc
|
|
80
|
+
instance_exec(&allowed_ips) #: Array[String]
|
|
81
|
+
else
|
|
82
|
+
Array(allowed_ips)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @rbs client_ip: String?
|
|
87
|
+
# @rbs return: void
|
|
88
|
+
def log_ip_denial(client_ip)
|
|
89
|
+
return unless ActionIpFilter.configuration.log_denials
|
|
90
|
+
|
|
91
|
+
logger = ActionIpFilter.configuration.logger
|
|
92
|
+
return if logger.nil?
|
|
93
|
+
|
|
94
|
+
logger.warn do
|
|
95
|
+
"[ActionIpFilter] Access denied for IP: #{client_ip} on #{self.class.name}##{action_name}"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ipaddr"
|
|
4
|
+
|
|
5
|
+
module ActionIpFilter
|
|
6
|
+
module IpMatcher
|
|
7
|
+
class << self
|
|
8
|
+
# @rbs client_ip: String?
|
|
9
|
+
# @rbs allowed_ips: Array[String]
|
|
10
|
+
# @rbs return: bool
|
|
11
|
+
def allowed?(client_ip, allowed_ips)
|
|
12
|
+
return false if client_ip.nil? || client_ip.empty? || allowed_ips.empty?
|
|
13
|
+
|
|
14
|
+
client_addr = parse_ip(client_ip)
|
|
15
|
+
return false unless client_addr
|
|
16
|
+
|
|
17
|
+
allowed_ips.any? do |allowed_ip|
|
|
18
|
+
match?(client_addr, allowed_ip)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# @rbs ip_string: String
|
|
25
|
+
# @rbs return: IPAddr?
|
|
26
|
+
def parse_ip(ip_string)
|
|
27
|
+
IPAddr.new(ip_string)
|
|
28
|
+
rescue IPAddr::InvalidAddressError
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @rbs client_addr: IPAddr
|
|
33
|
+
# @rbs allowed_ip: String
|
|
34
|
+
# @rbs return: bool
|
|
35
|
+
def match?(client_addr, allowed_ip)
|
|
36
|
+
if allowed_ip.include?("/")
|
|
37
|
+
range = IPAddr.new(allowed_ip)
|
|
38
|
+
range.include?(client_addr)
|
|
39
|
+
else
|
|
40
|
+
allowed_addr = IPAddr.new(allowed_ip)
|
|
41
|
+
client_addr == allowed_addr
|
|
42
|
+
end
|
|
43
|
+
rescue IPAddr::InvalidAddressError
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionIpFilter
|
|
4
|
+
module TestHelpers
|
|
5
|
+
# @rbs [T] () { () -> T } -> T
|
|
6
|
+
def without_ip_filter(&block)
|
|
7
|
+
original_mode = ActionIpFilter.test_mode?
|
|
8
|
+
ActionIpFilter.test_mode = true
|
|
9
|
+
block.call
|
|
10
|
+
ensure
|
|
11
|
+
ActionIpFilter.test_mode = original_mode
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @rbs [T] () { () -> T } -> T
|
|
15
|
+
def with_ip_filter(&block)
|
|
16
|
+
original_mode = ActionIpFilter.test_mode?
|
|
17
|
+
ActionIpFilter.test_mode = false
|
|
18
|
+
block.call
|
|
19
|
+
ensure
|
|
20
|
+
ActionIpFilter.test_mode = original_mode
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs return: void
|
|
24
|
+
def enable_ip_filter_test_mode!
|
|
25
|
+
ActionIpFilter.test_mode = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @rbs return: void
|
|
29
|
+
def disable_ip_filter_test_mode!
|
|
30
|
+
ActionIpFilter.test_mode = false
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support"
|
|
4
|
+
require "active_support/core_ext/object/blank"
|
|
5
|
+
|
|
6
|
+
require_relative "action_ip_filter/version"
|
|
7
|
+
require_relative "action_ip_filter/configuration"
|
|
8
|
+
require_relative "action_ip_filter/ip_matcher"
|
|
9
|
+
require_relative "action_ip_filter/ip_filterable"
|
|
10
|
+
require_relative "action_ip_filter/test_helpers"
|
|
11
|
+
require_relative "action_ip_filter/railtie" if defined?(Rails::Railtie)
|
|
12
|
+
|
|
13
|
+
module ActionIpFilter
|
|
14
|
+
# @rbs!
|
|
15
|
+
# def self.test_mode=: (bool) -> bool
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
# @rbs @test_mode: bool
|
|
19
|
+
|
|
20
|
+
attr_accessor :test_mode #: bool
|
|
21
|
+
|
|
22
|
+
# @rbs return: bool
|
|
23
|
+
def test_mode?
|
|
24
|
+
@test_mode == true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
self.test_mode = false
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: action_ip_filter
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- SmartBank, Inc.
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rake
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '13.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '13.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rspec
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec-rails
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '8.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '8.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: standard
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.52'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.52'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rails
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '8.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '8.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: rbs-inline
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0.12'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0.12'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: steep
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '1.10'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '1.10'
|
|
110
|
+
description: A lightweight concern that allows IP address restrictions on specific
|
|
111
|
+
controller actions. Unlike Rack middleware solutions, this operates at the controller
|
|
112
|
+
level for minimal overhead.
|
|
113
|
+
email:
|
|
114
|
+
- moznion@mail.moznion.net
|
|
115
|
+
executables: []
|
|
116
|
+
extensions: []
|
|
117
|
+
extra_rdoc_files: []
|
|
118
|
+
files:
|
|
119
|
+
- CHANGELOG.md
|
|
120
|
+
- LICENSE.txt
|
|
121
|
+
- README.md
|
|
122
|
+
- lib/action_ip_filter.rb
|
|
123
|
+
- lib/action_ip_filter/configuration.rb
|
|
124
|
+
- lib/action_ip_filter/ip_filterable.rb
|
|
125
|
+
- lib/action_ip_filter/ip_matcher.rb
|
|
126
|
+
- lib/action_ip_filter/railtie.rb
|
|
127
|
+
- lib/action_ip_filter/test_helpers.rb
|
|
128
|
+
- lib/action_ip_filter/version.rb
|
|
129
|
+
homepage: https://github.com/smartbank-inc/action_ip_filter
|
|
130
|
+
licenses:
|
|
131
|
+
- MIT
|
|
132
|
+
metadata:
|
|
133
|
+
homepage_uri: https://github.com/smartbank-inc/action_ip_filter
|
|
134
|
+
source_code_uri: https://github.com/smartbank-inc/action_ip_filter
|
|
135
|
+
changelog_uri: https://github.com/smartbank-inc/action_ip_filter/blob/main/CHANGELOG.md
|
|
136
|
+
rdoc_options: []
|
|
137
|
+
require_paths:
|
|
138
|
+
- lib
|
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - ">="
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: 3.4.0
|
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
|
+
requirements:
|
|
146
|
+
- - ">="
|
|
147
|
+
- !ruby/object:Gem::Version
|
|
148
|
+
version: '0'
|
|
149
|
+
requirements: []
|
|
150
|
+
rubygems_version: 3.6.7
|
|
151
|
+
specification_version: 4
|
|
152
|
+
summary: IP address restriction concern for Rails controllers
|
|
153
|
+
test_files: []
|