Permalink
Browse files

Allow a multitude of governesses

  • Loading branch information...
1 parent 1a4ab08 commit b7b10941284f9d4b5d3c5441a394c8ff4961a114 @coffeeaddict committed Oct 24, 2012
View
1 .rspec
@@ -0,0 +1 @@
+--color -f d
View
@@ -4,7 +4,7 @@
require "kindergarten/version"
require "kindergarten/sandbox"
require "kindergarten/exceptions"
-require "kindergarten/governess"
+require "kindergarten/governesses"
require "kindergarten/perimeter"
module Kindergarten
@@ -0,0 +1,19 @@
+module Kindergarten
+ # Hash with only allowed keys
+ class ScrubbedHash < Hash; end
+
+ # Hash with only allowed keys and untainted values
+ class RinsedHash < Hash; end
+
+ module Governesses
+ class << self
+ attr_accessor :forbidden_keys
+ end
+
+ self.forbidden_keys = []
+ end
+end
+
+require "kindergarten/governesses/head_governess"
+require "kindergarten/governesses/strict_governess"
+require "kindergarten/governesses/easy_governess"
@@ -0,0 +1,11 @@
+module Kindergarten
+ # A very easy governess, lets everything happen unguarded. Perhaps not such
+ # a good idea to be using this...
+ #
+ class EasyGoverness < HeadGoverness
+ def initialize(child)
+ super
+ @unguarded = true
+ end
+ end
+end
@@ -1,47 +1,45 @@
module Kindergarten
- # Hash with only allowed keys
- class ScrubbedHash < Hash; end
-
- # Hash with only allowed keys and untainted values
- class RinsedHash < Hash; end
-
# The Governess keeps an eye on the child in the sandbox and makes sure
# she plays nicely and within the bounds of legality
- #
- class Governess
- class << self
- attr_accessor :forbidden_keys
- end
-
- self.forbidden_keys = []
-
- # Check how often we guarded something
- attr_reader :guard_count
-
+ #
+ class HeadGoverness
include CanCan::Ability
def initialize(child)
- @child = child
- @guard_count = 0
- @unguarded = false
- @rules = []
+ @child = child
+ @unguarded = false
+ @rules = []
end
# The governess is empty when no rules have been defined
def empty?
@rules.empty?
end
-
- # Check to see if the child can do something, increments @guard_count
+
+ # Perform a sandbox method within the care of the governess.
+ #
+ # The HeadGoverness does nothing with it.
+ #
+ # @param [Symbol] method The name of the method that will be executed (for
+ # logging, record-keeping, raising, etc.)
+ # @return The result of the block
+ #
+ def governed(method, &block)
+ raise "You must specify a block" unless block_given?
+
+ return yield
+ end
+
+ # Check to see if the child can do something, increments @guard_count
#
# @param action Action to take
# @param target On given target
# @param opts [Hash] options
- # @option opts [String] :message The message on access denied
+ # @option opts [String] :message The message on access denied
#
# @raise [Kindergarten::AccessDenied] when the kindergarten is guarded and
# the action is not allowed
- #
+ #
# @return The given target to allow
# def project(id)
# project = Project.find(id)
@@ -50,11 +48,9 @@ def empty?
#
def guard(action, target, opts={})
if guarded? && cannot?(action, target)
- raise Kindergarten::AccessDenied.new(action, target, opts)
+ raise Kindergarten::AccessDenied.new(action, target, opts)
end
- @guard_count += 1
-
# to allow
# def project(id)
# project = Project.find(id)
@@ -64,17 +60,16 @@ def guard(action, target, opts={})
return target
end
- # Increment the @guard_count without looking.
- #
- # When a block is given, set the Governess to unguarded during the
+ # When a block is given, set the Governess to unguarded during the
# execution of the block
#
def unguarded(&block)
- @guard_count += 1
if block_given?
+ before = @unguarded
+
@unguarded = true
yield
- @unguarded = false
+ @unguarded = before
end
end
@@ -85,7 +80,7 @@ def guarded?
def unguarded?
!!@unguarded
end
-
+
# Scrub a hash of any key that is not specified
#
# @param [Hash] attributes An attributes-hash to scrub
@@ -94,22 +89,24 @@ def unguarded?
# @return [ScrubbedHash] a hash with only allowed keys
def scrub(attributes, *list)
list.map!(&:to_sym)
-
+
+ forbidden = Kindergarten::Governesses.forbidden_keys
+
Kindergarten::ScrubbedHash[
attributes.symbolize_keys!.delete_if do |key,value|
- self.class.forbidden_keys.include?(key) || !list.include?(key)
+ forbidden.include?(key) || !list.include?(key)
end
]
end
# Scrub a hash of any key that is not specified
#
# @param [Hash] attributes An attributes-hash to scrub
- # @param [Hash] untaint_opts Specify a Regexp for each key. The value from
- # the attributes will be matched against the regexp and replaced with
+ # @param [Hash] untaint_opts Specify a Regexp for each key. The value from
+ # the attributes will be matched against the regexp and replaced with
# the first result.
#
- # specify :pass instead of a Regexp to let the value pass without
+ # specify :pass instead of a Regexp to let the value pass without
# matching (usefull for non strings, etc.)
#
# @return [RinsedHash] a hash with only allowed keys and untainted values
@@ -138,9 +135,9 @@ def rinse(attributes, untaint_opts)
scrubbed.delete key
else
scrubbed[key] = match[1]
- end
+ end
end
-
+
return Kindergarten::RinsedHash[scrubbed]
end
end
@@ -0,0 +1,48 @@
+module Kindergarten
+ # A very strict governess, forces all the sandbox methods to use the guard
+ # methods.
+ #
+ # @note
+ # Does not specify how to rollback when the guard method was not called,
+ # You're on your own there...
+ #
+ class StrictGoverness < HeadGoverness
+ # Check how often the perimeter guarded something
+ attr_reader :guard_count
+
+ def initialize(child)
+ super
+ @guard_count = 0
+ end
+
+ # Force the use of guard inside sandbox methods
+ #
+ # @raise Kindergarten::Perimeter::Unguarded when the guard count did not
+ # increment during the block execution
+ #
+ def governed(method, &block)
+ before = self.guard_count
+ res = yield
+
+ if @unguarded != true && self.guard_count == before
+ raise Kindergarten::Perimeter::Unguarded.new(
+ "#{method} was executed without propper guarding"
+ )
+ end
+
+ return res
+ end
+
+ # guard something and increment the guard count
+ def guard(*args)
+ @guard_count += 1
+ super
+ end
+
+ # allow something unguarded and increment the guard count
+ def unguarded(&block)
+ @guard_count += 1
+ super
+ end
+ end
+end
@@ -33,20 +33,25 @@ def sandbox(*list)
def govern(&proc)
@govern_proc = proc
end
-
+
# Get/set the purpose of the perimeter
def purpose(*purpose)
purpose.any? ? @purpose = purpose[0] : @purpose
end
-
+
+ # Get/set the governess of the perimeter
+ def governess(*klass)
+ klass.any? ? @governess = klass[0] : @governess
+ end
+
# Subscribe to an event from a given purpose
- # @param [Symbol] purpose Listen to other perimeters that have this
+ # @param [Symbol] purpose Listen to other perimeters that have this
# purpose
# @param [Symbol] event Listen for events with this name
# @param [Proc,Symbol] block Invoke this on the event
# @example Symbol form
# subscribe :users, :create, :user_created
- #
+ #
# def user_created(event)
# # ...
# end
@@ -73,17 +78,17 @@ def subscribe(purpose, event, block)
def self.instance(child=nil, governess=nil)
self.new(child, governess)
end
-
+
def initialize(child, governess)
@child = child
@governess = governess
-
+
unless @governess.nil?
- @governess.instance_eval &self.class.govern_proc
+ @governess.instance_eval &self.class.govern_proc
end
end
-
- # @return [Array] List of sandbox methods
+
+ # @return [Array] List of sandbox methods
def sandbox_methods
self.class.sandboxed_methods
end
@@ -92,20 +97,33 @@ def sandbox_methods
def scrub(*args)
self.governess.scrub(*args)
end
-
+
# @see Governess#rinse
def rinse(*args)
self.governess.rinse(*args)
end
-
+
# @see Governess#guard
def guard(action, target)
self.governess.guard(action, target)
end
-
+
# @see Governess#unguarded
def unguarded(&block)
self.governess.unguarded(&block)
end
+
+ def governed(method, unguarded=false, &block)
+ if unguarded == true
+ self.governess.unguarded do
+ self.governess.governed(method, &block)
+ end
+
+ else
+ self.governess.governed(method, &block)
+
+ end
+ end
+
end
end
@@ -4,7 +4,7 @@ class Sandbox
def initialize(child)
@child = child
- @governess = Kindergarten::Governess.new(child)
+ @governess = Kindergarten::HeadGoverness.new(child)
@perimeter = []
def @perimeter.include?(other)
@@ -16,7 +16,19 @@ def @perimeter.include?(other)
def extend_perimeter(*perimeter_classes)
perimeter_classes.each do |perimeter_class|
- perimeter = perimeter_class.new(self.child, self.governess)
+ # if the perimeter specifies a governess, use that - otherwise appoint
+ # the head governess
+ child = self.child
+ governess = perimeter_class.governess ?
+ perimeter_class.governess.new(child) :
+ self.governess
+
+ perimeter = perimeter_class.new(child, governess)
+
+ # the head governess must know all the rules
+ unless governess == self.governess
+ self.governess.instance_eval &perimeter_class.govern_proc
+ end
raise ArgumentError.new(
"Module must inherit from Kindergarten::Perimeter"
@@ -32,25 +44,14 @@ def unguarded(&block)
@unguarded = false
end
- def force_guard(name, &block)
- before = governess.guard_count
- res = yield
-
- if @unguarded != true && governess.guard_count == before
- raise Kindergarten::Perimeter::Unguarded.new(
- "#{name} was executed without propper guarding"
- )
- end
-
- return res
- end
-
def method_missing(name, *args, &block)
super
rescue NoMethodError => ex
@perimeter.each do |perimeter|
if perimeter.sandbox_methods.include?(name)
- return force_guard(name) { perimeter.send(name, *args, &block) }
+ return perimeter.governed(name, @unguarded) do
+ perimeter.send(name, *args, &block)
+ end
end
end
Oops, something went wrong.

0 comments on commit b7b1094

Please sign in to comment.