Skip to content

Commit

Permalink
Merge 9cf92b7 into 9bada2a
Browse files Browse the repository at this point in the history
  • Loading branch information
0exp committed Aug 13, 2018
2 parents 9bada2a + 9cf92b7 commit f698d16
Show file tree
Hide file tree
Showing 17 changed files with 793 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ All notable changes to this project will be documented in this file.
### Added
- `Rack::BlastWave::RequestId` - rack middleware that provides an unique id attribute
to the each rack request object and rack `#env` scope;
- `Rack::BlastWave::WhiteList` - rack middleware that blocks or allows requests
by filtering them via predefined list of filters: failed filters blocks current request,
successful filters allows current request.
- `Rack::BlastWave::BlackList` - rack middleware that blocks or allows requests
by filtering them via predefined list of filters: failed filters allows current request,
successful filters blocks current request.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ require 'blast_wave'
## Usage

- [Rack::BlastWave::RequestId](#rackblastwaverequestid) - provides an unique id attribute to the each request;
- [Rack::BlastWave::WhiteList](#rackblastwavewhitelist) - a set of filters that allows requests;
- [Rack::BlastWave::BlackList](#rackblastwaveblacklist) - a set of filters that blocks requests;

## Contributing

Expand Down
5 changes: 3 additions & 2 deletions blast_wave.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ Gem::Specification.new do |spec|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|features)/}) }
end

spec.add_dependency 'rack'
spec.add_dependency 'qonfig'
spec.add_dependency 'rack', '~> 2.0'
spec.add_dependency 'qonfig', '~> 0.5'
spec.add_dependency 'concurrent-ruby', '~> 1.0'

spec.add_development_dependency 'coveralls', '~> 0.8'
spec.add_development_dependency 'simplecov', '~> 0.16'
Expand Down
6 changes: 4 additions & 2 deletions lib/blast_wave.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'rack'
require 'securerandom'
require 'qonfig'
require 'concurrent/map'

module Rack
# @api public
Expand All @@ -11,7 +12,8 @@ module BlastWave
require_relative 'blast_wave/version'
require_relative 'blast_wave/middleware'
require_relative 'blast_wave/request_id'
require_relative 'blast_wave/request_id/extensions/request_interface'
require_relative 'blast_wave/request_id/initializer'
require_relative 'blast_wave/check_list'
require_relative 'blast_wave/white_list'
require_relative 'blast_wave/black_list'
end
end
16 changes: 16 additions & 0 deletions lib/blast_wave/black_list.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module Rack
# @api public
# @since 0.1.0
class BlastWave::BlackList < BlastWave::CheckList
# @param env [Hash]
# @return [Object]
#
# @api private
# @since 0.1.0
def call(env)
check!(env) ? generate_fail_response! : super
end
end
end
35 changes: 35 additions & 0 deletions lib/blast_wave/check_list.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Rack
# @api private
# @since 0.1.0
class BlastWave::CheckList < BlastWave::Middleware
require_relative 'check_list/filter'
require_relative 'check_list/filter_registry'
require_relative 'check_list/fail_response'
require_relative 'check_list/checkable'
require_relative 'check_list/checker'
require_relative 'check_list/builder'

class << self
# Creates a clone of the current middleware with it's own initial settings.
#
# @return [Class<BlastWave::CheckList>]
#
# @api public
# @since 0.1.0
def build
Class.new(self)
end

# @param child_klass [Class]
# @return [void]
#
# @api private
# @since 0.1.0
def inherited(child_klass)
BlastWave::CheckList::Builder.build(child_klass)
end
end
end
end
78 changes: 78 additions & 0 deletions lib/blast_wave/check_list/builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# frozen_string_literal: true

module Rack
# @api public
# @since 0.1.0
module BlastWave::CheckList::Builder
# @return [Proc]
#
# @api private
# @since 0.1.0
# rubocop:disable Metrics/BlockLength
CLASS_DEFINITIONS = proc do |check_all: false|
# @since 0.1.0
include Qonfig::Configurable
# @since 0.1.0
include BlastWave::CheckList::Checkable

# @since 0.1.0
configuration do
setting :check_all, check_all

setting :fail_response do
setting :body, BlastWave::CheckList::FailResponse::DEFAULT_BODY
setting :status, BlastWave::CheckList::FailResponse::DEFAULT_STATUS
setting :headers, BlastWave::CheckList::FailResponse::DEFAULT_HEADERS
end
end

# @return [Qonfig::Settings]
#
# @api private
# @since 0.1.0
def options
self.class.config.settings
end

private

# @param env [Hash]
# @return [Boolean]
#
# @api private
# @since 0.1.0
def check!(env)
request = Rack::Request.new(env)
should_check_all = options.check_all?
checker.check!(request, check_all: should_check_all)
end

# @return [Rack::Response]
#
# @api private
# @since 0.1.0
def generate_fail_response!
BlastWave::CheckList::FailResponse.build(
status: options.fail_response.status,
body: options.fail_response.body,
headers: options.fail_response.headers
).finish
end
end
# rubocop:enable Metrics/BlockLength

class << self
# @param empty_middleware_klass [Class<BlastWave::MiddleWare>]
# @return [Class<BlastWave::MiddleWare>]
#
# @api public
# @since 0.1.0
def build(empty_middleware_klass = Class.new(BlastWave::Middleware), &additionals)
empty_middleware_klass.tap do |middleware_klass|
middleware_klass.class_eval(&CLASS_DEFINITIONS)
middleware_klass.class_eval(&additionals) if block_given?
end
end
end
end
end
54 changes: 54 additions & 0 deletions lib/blast_wave/check_list/checkable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

module Rack
# @api private
# @since 0.1.0
module BlastWave::CheckList::Checkable
class << self
# @param base_klass [Class]
# @return [void]
#
# @api private
# @since 0.1.0
def included(base_klass)
base_klass.extend(ClassMethods)
base_klass.include(InstanceMethods)
base_klass.instance_variable_set(:@checker, BlastWave::CheckList::Checker.new)
base_klass.singleton_class.class_eval { attr_reader :checker }
end
end

# @api private
# @since 0.1.0
module ClassMethods
# @param block [Proc]
# @return [void]
#
# @api public
# @since 0.1.0
def filter(name = nil, &block)
block_given? ? checker.register(name || block.object_id, &block) : checker.fetch(name)
end

# @return [void]
#
# @api public
# @since 0.1.0
def clear!
checker.clear!
end
end

# @api private
# @since 0.1.0
module InstanceMethods
# @return [BlastWave::CheckLis::Checker]
#
# @api private
# @sicne 0.1.0
def checker
self.class.checker
end
end
end
end
77 changes: 77 additions & 0 deletions lib/blast_wave/check_list/checker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

module Rack
# @api private
# @since 0.1.0
class BlastWave::CheckList::Checker
# @return [void]
#
# @api private
# @since 0.1.0
def initialize
@filter_registry = BlastWave::CheckList::FilterRegistry.new
end

# @param request [Rack::Request]
# @option check_all [Boolean]
# @return [Boolean]
#
# @api private
# @since 0.1.0
def check!(request, check_all: false)
check_all ? all?(request) : any?(request)
end

# @return [void]
#
# @api private
# @since 0.1.0
def clear!
filter_registry.clear!
end

# @param block [Proc]
# @return [void]
#
# @api private
# @since 0.1.0
def register(name, &block)
filter_registry.register(BlastWave::CheckList::Filter.new(name, block))
end

# @param name [Object]
# @return [BlastWave::CheckList::Filter]
#
# @api private
# @since 0.1.0
def fetch(name)
filter_registry.fetch(name)
end

private

# @return [BlastWave::CheckList::FilterRegistry]
#
# @api private
# @since 0.1.0
attr_reader :filter_registry

# @param request [Rack::Request]
# @return [Boolean]
#
# @api private
# @since 0.1.0
def any?(request)
filter_registry.any? { |filter| filter.match?(request) }
end

# @param request [Rack::Request]
# @return [Boolean]
#
# @api private
# @since 0.1.0
def all?(request)
filter_registry.all? { |filter| filter.match?(request) }
end
end
end
49 changes: 49 additions & 0 deletions lib/blast_wave/check_list/fail_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

module Rack
# @api private
# @since 0.1.0
class BlastWave::CheckList::FailResponse < Rack::Response
# @return [Array<String>]
#
# @api private
# @since 0.1.0
DEFAULT_BODY = ["Forbidden\n"].freeze

# @return [Hash]
#
# @api private
# @since 0.1.0
DEFAULT_HEADERS = { 'Content-Type' => 'text/plain' }.freeze

# @return [Integer]
#
# @api private
# @since 0.1.0
DEFAULT_STATUS = 403

class << self
# @option status [Integer]
# @option body [Array<String>]
# @option headers [Hash]
# @return [Rack::Response]
#
# @api private
# @since 0.1.0
def build(status: DEFAULT_STATUS, body: DEFAULT_BODY, headers: DEFAULT_HEADERS)
new(body, status, headers)
end
end

# @param body [Array<String>]
# @param status [Integer]
# @param header [Hash]
# @return [void]
#
# @since 0.1.0
# @api private
def initialize(body = DEFAULT_BODY, status = DEFAULT_STATUS, header = DEFAULT_HEADERS)
super
end
end
end

0 comments on commit f698d16

Please sign in to comment.