Initial commit
This commit is contained in:
commit
5a205201aa
|
@ -0,0 +1,16 @@
|
|||
/.bundle/
|
||||
/.yardoc
|
||||
/Gemfile.lock
|
||||
/_yardoc/
|
||||
/coverage/
|
||||
/doc/
|
||||
/pkg/
|
||||
/spec/reports/
|
||||
/tmp/
|
||||
|
||||
# rspec failure tracking
|
||||
.rspec_status
|
||||
|
||||
.byebug_history
|
||||
|
||||
*.gem
|
|
@ -0,0 +1,5 @@
|
|||
sudo: false
|
||||
language: ruby
|
||||
rvm:
|
||||
- 2.4.1
|
||||
before_install: gem install bundler -v 1.14.6
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Rob Watson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,58 @@
|
|||
# rack-filter-param
|
||||
|
||||
Refactoring something behind an API? Plagued by extraneous HTTP params? `rack-filter-param` might be for you.
|
||||
|
||||
## What is it?
|
||||
|
||||
[Rack](https://github.com/rack/rack) middleware to remove specific params from HTTP requests.
|
||||
|
||||
## What does it do?
|
||||
|
||||
Given a set of params and optional constraints, `rack-filter-param` will remove those params, then pass the request downstream.
|
||||
|
||||
Removes params from:
|
||||
|
||||
* GET querystring
|
||||
* POST params (x-www-form-urlencoded)
|
||||
* JSON or other params hitherto processed by [`ActionDispatch::ParamsParser`](http://api.rubyonrails.org/classes/ActionDispatch/ParamsParser.html)
|
||||
|
||||
## Installation
|
||||
|
||||
```ruby
|
||||
|
||||
gem 'rack-filter-param', require: 'rack/filter_param'
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
In rackup file or `application.rb`, initialize `rack-filter-param` with a list of HTTP params you want filtered from requests.
|
||||
|
||||
Strip a parameter named `client_id`:
|
||||
|
||||
```ruby
|
||||
use Rack::FilterParam, :client_id
|
||||
```
|
||||
|
||||
Strip a parameter named `client_id` from a specific path only:
|
||||
|
||||
```ruby
|
||||
use Rack::FilterParam, { param: :client_id, path: '/oauth/tokens' }
|
||||
```
|
||||
|
||||
Strip a parameter named `client_id` from a fuzzy path:
|
||||
|
||||
```ruby
|
||||
use Rack::FilterParam, { param: :client_id, path: /\A\/oauth/ }
|
||||
```
|
||||
|
||||
To filter multiple parameters, an array of parameters or options hashes can also be passed.
|
||||
|
||||
## Contributing
|
||||
|
||||
Bug reports and pull requests are welcome on GitHub at https://github.com/rfwatson/rack-filter-param
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
require "bundler/gem_tasks"
|
||||
require "rspec/core/rake_task"
|
||||
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
|
||||
task :default => :spec
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require "bundler/setup"
|
||||
require "rack/filter_param"
|
||||
|
||||
# You can add fixtures and/or initialization code here to make experimenting
|
||||
# with your gem easier. You can also use a different console, if you like.
|
||||
|
||||
# (If you use this, don't forget to add pry to your Gemfile!)
|
||||
# require "pry"
|
||||
# Pry.start
|
||||
|
||||
require "irb"
|
||||
IRB.start(__FILE__)
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
set -vx
|
||||
|
||||
bundle install
|
||||
|
||||
# Do any other automated setup that you need to do here
|
|
@ -0,0 +1,66 @@
|
|||
require "rack/filter_param/version"
|
||||
|
||||
module Rack
|
||||
class FilterParam
|
||||
ACTION_DISPATCH_KEY = 'action_dispatch.request.request_parameters'.freeze
|
||||
FILTERED_PARAMS_KEY = 'rack.filtered_params'.freeze
|
||||
|
||||
def initialize(app, *params)
|
||||
@app = app
|
||||
@params = params.flatten
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@request = Rack::Request.new(env)
|
||||
@params.each { |param| process_param(param) }
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :request
|
||||
|
||||
def process_param(param)
|
||||
return unless path_matches?(param)
|
||||
|
||||
param = param[:param] if param.is_a?(Hash)
|
||||
|
||||
if delete_from_action_dispatch(param) || delete_from_request(param)
|
||||
filtered_params << [ param.to_s, nil ]
|
||||
end
|
||||
end
|
||||
|
||||
def path_matches?(param)
|
||||
return true unless param.is_a?(Hash)
|
||||
|
||||
path = param[:path]
|
||||
return true unless path = param[:path]
|
||||
|
||||
return request.env['PATH_INFO'] == path if path.is_a?(String)
|
||||
return request.env['PATH_INFO'] =~ path if path.is_a?(Regexp)
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def delete_from_action_dispatch(param)
|
||||
action_dispatch_parsed? && !!action_dispatch_params.delete(param.to_s)
|
||||
end
|
||||
|
||||
def delete_from_request(param)
|
||||
!!request.delete_param(param.to_s)
|
||||
end
|
||||
|
||||
def action_dispatch_params
|
||||
request.env[ACTION_DISPATCH_KEY]
|
||||
end
|
||||
|
||||
def action_dispatch_parsed?
|
||||
!action_dispatch_params.nil?
|
||||
end
|
||||
|
||||
def filtered_params
|
||||
request.env[FILTERED_PARAMS_KEY] ||= []
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
module Rack
|
||||
class FilterParam
|
||||
VERSION = "0.1.0"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
# coding: utf-8
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'rack/filter_param/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "rack-filter-param"
|
||||
spec.version = Rack::FilterParam::VERSION
|
||||
spec.authors = ["Rob Watson"]
|
||||
spec.email = ["rob@mixlr.com"]
|
||||
|
||||
spec.summary = "Rack middleware to filter params from HTTP requests"
|
||||
spec.homepage = "https://github.com/rfwatson"
|
||||
spec.license = "MIT"
|
||||
|
||||
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
||||
f.match(%r{^(test|spec|features)/})
|
||||
end
|
||||
spec.bindir = "exe"
|
||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_development_dependency 'bundler', '~> 1.14'
|
||||
spec.add_development_dependency 'rake', '~> 10.0'
|
||||
spec.add_development_dependency 'rspec', '~> 3.0'
|
||||
spec.add_development_dependency 'rack-test', '~> 0.6'
|
||||
spec.add_development_dependency 'json', '~> 2'
|
||||
spec.add_development_dependency 'byebug', '~> 9'
|
||||
end
|
|
@ -0,0 +1,190 @@
|
|||
require "spec_helper"
|
||||
require 'json'
|
||||
|
||||
RSpec.describe Rack::FilterParam do
|
||||
let(:path) { '/' }
|
||||
let(:params) { {} }
|
||||
let(:headers) { {} }
|
||||
let(:rack_env) { {} }
|
||||
let(:params_to_test) {
|
||||
last_request.env[described_class::ACTION_DISPATCH_KEY] || last_request.params
|
||||
}
|
||||
|
||||
before {
|
||||
headers.each { |k, v| header(k.to_s, v) }
|
||||
public_send(method, path, params, rack_env)
|
||||
}
|
||||
|
||||
shared_context 'middleware with basic filters' do
|
||||
let(:app) {
|
||||
Rack::Builder.new do
|
||||
use Rack::FilterParam, :x, :y
|
||||
run -> (env) { [200, {}, ['OK']] }
|
||||
end.to_app
|
||||
}
|
||||
end
|
||||
|
||||
shared_context 'middleware with filtered paths' do
|
||||
let(:app) {
|
||||
Rack::Builder.new do
|
||||
use Rack::FilterParam, [
|
||||
{ param: :x, path: '/' },
|
||||
{ param: :y, path: /\A\/something/ }
|
||||
]
|
||||
run -> (env) { [200, {}, ['OK']] }
|
||||
end.to_app
|
||||
}
|
||||
end
|
||||
|
||||
shared_examples 'core functionality' do
|
||||
context 'sending a param that is not expected to be filtered' do
|
||||
let(:params) { { 'a' => '1' } }
|
||||
|
||||
it 'does not filter the param' do
|
||||
expect(params_to_test).to eq('a' => '1')
|
||||
end
|
||||
|
||||
it 'does not include the param in `rack.filtered_params`' do
|
||||
expect(last_request.env['rack.filtered_params'])
|
||||
.to be nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'sending a param that is expected to be filtered' do
|
||||
let(:params) { { 'x' => '1' } }
|
||||
|
||||
it 'filters the param' do
|
||||
expect(params_to_test.keys).to eq []
|
||||
end
|
||||
|
||||
it 'includes the param in `rack.filtered_params`' do
|
||||
expect(last_request.env['rack.filtered_params'])
|
||||
.to eq [['x', nil]]
|
||||
end
|
||||
end
|
||||
|
||||
context 'sending two params, filtering one' do
|
||||
let(:params) { { 'x' => '1', 'a' => '1' } }
|
||||
|
||||
it 'filters the param' do
|
||||
expect(params_to_test.keys).to eq ['a']
|
||||
end
|
||||
|
||||
it 'includes one param in `rack.filtered_params`' do
|
||||
expect(last_request.env['rack.filtered_params'])
|
||||
.to eq [['x', nil]]
|
||||
end
|
||||
end
|
||||
|
||||
context 'sending three params, filtering two' do
|
||||
let(:params) { { 'x' => '1', 'y' => '1', 'a' => '1' } }
|
||||
|
||||
it 'filters the params' do
|
||||
expect(params_to_test.keys).to eq ['a']
|
||||
end
|
||||
|
||||
it 'includes two params in `rack.filtered_params`' do
|
||||
expect(last_request.env['rack.filtered_params'])
|
||||
.to eq [['x', nil], ['y', nil]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'path filtering' do
|
||||
let(:params) { { 'x' => '1', 'y' => '1' } }
|
||||
|
||||
context 'when the path is equal to a string' do
|
||||
let(:path) { '/' }
|
||||
|
||||
it 'filters the param' do
|
||||
expect(params_to_test.keys).to eq ['y']
|
||||
end
|
||||
|
||||
it 'includes the param in `rack.filtered_params`' do
|
||||
expect(last_request.env['rack.filtered_params'])
|
||||
.to eq [['x', nil]]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the path matches a regexp' do
|
||||
let(:path) { '/something/good' }
|
||||
|
||||
it 'filters the param' do
|
||||
expect(params_to_test.keys).to eq ['x']
|
||||
end
|
||||
|
||||
it 'includes the param in `rack.filtered_params`' do
|
||||
expect(last_request.env['rack.filtered_params'])
|
||||
.to eq [['y', nil]]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the path does not match' do
|
||||
let(:path) { '/wrong' }
|
||||
|
||||
it 'does not filter the param' do
|
||||
expect(params_to_test)
|
||||
.to eq('x' => '1', 'y' => '1')
|
||||
end
|
||||
|
||||
it 'does not include the param in `rack.filtered_params`' do
|
||||
expect(last_request.env['rack.filtered_params'])
|
||||
.to be nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'GET request' do
|
||||
let(:method) { :get }
|
||||
|
||||
describe 'basic functionality' do
|
||||
include_context 'middleware with basic filters'
|
||||
include_examples 'core functionality'
|
||||
end
|
||||
|
||||
describe 'path filtering' do
|
||||
include_context 'middleware with filtered paths'
|
||||
include_examples 'path filtering'
|
||||
end
|
||||
end
|
||||
|
||||
context 'POST request' do
|
||||
let(:method) { :post }
|
||||
|
||||
let(:headers) {
|
||||
super().merge(
|
||||
'Content-Type' => 'application/x-www-form-urlencoded'
|
||||
)
|
||||
}
|
||||
|
||||
describe 'basic functionality' do
|
||||
include_context 'middleware with basic filters'
|
||||
include_examples 'core functionality'
|
||||
end
|
||||
|
||||
describe 'path filtering' do
|
||||
include_context 'middleware with filtered paths'
|
||||
include_examples 'path filtering'
|
||||
end
|
||||
end
|
||||
|
||||
context 'Request previously parsed by ActionDispatch::ParamsParser' do
|
||||
let(:method) { :post }
|
||||
let(:headers) { super().merge('Content-Type' => 'application/json') }
|
||||
let(:params) { super().to_json }
|
||||
|
||||
let(:rack_env) {
|
||||
{ described_class::ACTION_DISPATCH_KEY => params }
|
||||
}
|
||||
|
||||
describe 'basic functionality' do
|
||||
include_context 'middleware with basic filters'
|
||||
include_examples 'core functionality'
|
||||
end
|
||||
|
||||
describe 'path filtering' do
|
||||
include_context 'middleware with filtered paths'
|
||||
include_examples 'path filtering'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
require 'bundler/setup'
|
||||
require 'rack/filter_param'
|
||||
require 'rack/test'
|
||||
require 'byebug'
|
||||
require 'ap'
|
||||
|
||||
RSpec.configure do |config|
|
||||
# Enable flags like --only-failures and --next-failure
|
||||
config.example_status_persistence_file_path = '.rspec_status'
|
||||
|
||||
# Include rack-test helpers
|
||||
config.include Rack::Test::Methods
|
||||
|
||||
config.expect_with :rspec do |c|
|
||||
c.syntax = :expect
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue