class MSS::Core::Policy::ConditionBlock
Represents the condition block of a policy. In JSON, condition blocks look like this:
{ "StringLike": { "s3:prefix": ["photos/*", "photos.html"] } }
ConditionBlock
lets you specify conditions like the above example using the add method, for example:
conditions.add(:like, :s3_prefix, "photos/*", "photos.html")
See the add method documentation for more details about how to specify keys and operators.
This class also provides a convenient way to query a condition block to see what operators, keys, and values it has. For example, consider the following condition block (in JSON):
{ "StringEquals": { "s3:prefix": "photos/index.html" }, "DateEquals": { "mss:CurrentTime": ["2010-10-12", "2011-01-02"] }, "NumericEquals": { "s3:max-keys": 10 } }
You can get access to the condition data using []
, keys
, operators
, and values
– for example:
conditions["DateEquals"]["mss:CurrentTime"].values # => ["2010-10-12", "2011-01-02"]
You can also perform more sophisticated queries, like this one:
conditions[:is].each do |equality_conditions| equality_conditions.keys.each do |key| puts("#{key} may be any of: " + equality_conditions[key].values.join(" ") end end
This would print the following lines:
s3:prefix may be any of: photos/index.html mss:CurrentTime may be any of: 2010-10-12 2011-01-02 s3:max-keys may be any of: 10
Constants
- MODIFIERS
@api private
Public Class Methods
@api private
# File lib/mss/core/policy.rb, line 296 def initialize(conditions = {}) # filter makes a copy @conditions = filter_conditions(conditions) end
Public Instance Methods
Filters the conditions described in the block, returning a new ConditionBlock
that contains only the matching conditions. Each argument is matched against either the keys or the operators in the block, and you can specify the key or operator in any way that's valid for the add
method. Some examples:
# all conditions using the StringLike operator conditions["StringLike"] # all conditions using StringEquals, DateEquals, NumericEquals, or Bool conditions[:is] # all conditions on the s3:prefix key conditions["s3:prefix"] # all conditions on the mss:CurrentTime key conditions[:current_time]
Multiple conditions are ANDed together, so the following are equivalent:
conditions[:s3_prefix][:is] conditions[:is][:s3_prefix] conditions[:s3_prefix, :is]
@see add
@return [ConditionBlock] A new set of conditions filtered by the
given conditions.
# File lib/mss/core/policy.rb, line 408 def [](*args) filtered = @conditions args.each do |filter| type = valid_operator?(filter) ? nil : :key filtered = filter_conditions(filtered) do |op, key, value| (match, type) = match_triple(filter, type, op, key, value) match end end self.class.new(filtered) end
Adds a condition to the block. This method defines a convenient set of abbreviations for operators based on the type of value passed in. For example:
conditions.add(:is, :secure_transport, true)
Maps to:
{ "Bool": { "mss:SecureTransport": true } }
While:
conditions.add(:is, :s3_prefix, "photos/")
Maps to:
{ "StringEquals": { "s3:prefix": "photos/" } }
The following list shows which operators are accepted as symbols and how they are represented in the JSON policy:
-
`:is` (StringEquals, NumericEquals, DateEquals, or Bool)
-
`:like` (StringLike)
-
`:not_like` (StringNotLike)
-
`:not` (StringNotEquals, NumericNotEquals, or DateNotEquals)
-
`:greater_than`, `:gt` (NumericGreaterThan or DateGreaterThan)
-
`:greater_than_equals`, `:gte` (NumericGreaterThanEquals or DateGreaterThanEquals)
-
`:less_than`, `:lt` (NumericLessThan or DateLessThan)
-
`:less_than_equals`, `:lte` (NumericLessThanEquals or DateLessThanEquals)
-
`:is_ip_address` (IpAddress)
-
`:not_ip_address` (NotIpAddress)
-
`:is_arn` (ArnEquals)
-
`:not_arn` (ArnNotEquals)
-
`:is_arn_like` (ArnLike)
-
`:not_arn_like` (ArnNotLike)
@param [Symbol or String] operator The operator used to
compare the key with the value. See above for valid values and their interpretations.
@param [Symbol or String] key The key to compare. Symbol
keys are inflected to match MSS conventions. By default, the key is assumed to be in the "mss" namespace, but if you prefix the symbol name with "s3_" it will be sent in the "s3" namespace. For example, `:s3_prefix` is sent as "s3:prefix" while `:secure_transport` is sent as "mss:SecureTransport". See for a list of the available keys for each action in S3.
@param [Mixed] values The value to compare against.
This can be: * a String * a number * a Date, DateTime, or Time * a boolean value This method does not attempt to validate that the values are valid for the operators or keys they are used with.
# File lib/mss/core/policy.rb, line 362 def add(operator, key, *values) if operator.kind_of?(Symbol) converted_values = values.map { |v| convert_value(v) } else converted_values = values end operator = translate_operator(operator, values.first) op = (@conditions[operator] ||= {}) raise "duplicate #{operator} conditions for #{key}" if op[key] op[translate_key(key)] = converted_values end
@return [Array] Returns an array of unique keys used in the block.
# File lib/mss/core/policy.rb, line 426 def keys @conditions.values.map do |keys| keys.keys if keys end.compact.flatten.uniq end
@return [Array] Returns an array of operators used in this block.
# File lib/mss/core/policy.rb, line 421 def operators @conditions.keys end
@api private
# File lib/mss/core/policy.rb, line 375 def to_h @conditions end
Returns all values used in the block. Note that the values may not all be from the same condition; for example:
conditions.add(:like, :user_agent, "mozilla", "explorer") conditions.add(:lt, :s3_max_keys, 12) conditions.values # => ["mozilla", "explorer", 12]
@return [Array] Returns an array of values used in this condition block.
# File lib/mss/core/policy.rb, line 440 def values @conditions.values.map do |keys| keys.values end.compact.flatten end
Protected Instance Methods
@api private
# File lib/mss/core/policy.rb, line 633 def base_translate(example, base_operator, *modifiers) "#{type_notation(example)}#{base_operator}#{modifiers.join}" end
@api private
# File lib/mss/core/policy.rb, line 654 def convert_value(value) case value when DateTime, Time Time.parse(value.to_s).iso8601 when Date value.strftime("%Y-%m-%d") else value end end
@api private
# File lib/mss/core/policy.rb, line 484 def filter_conditions(conditions = @conditions) conditions.inject({}) do |m, (op, keys)| m[op] = keys.inject({}) do |m2, (key, value)| m2[key] = value if !block_given? or yield(op, key, value) m2 end m.delete(op) if m[op].empty? m end end
@api private
# File lib/mss/core/policy.rb, line 478 def match_key(filter, key, value = nil) translate_key(filter) == key end
@api private
# File lib/mss/core/policy.rb, line 469 def match_operator(filter, op, value) # dates are the only values that don't come back as native types in JSON # but where we use the type as a cue to the operator translation value = Date.today if op =~ /^Date/ translate_operator(filter, value) == op end
@api private
# File lib/mss/core/policy.rb, line 448 def match_triple(filter, type, op, key, value) value = [value].flatten.first if type target = (type == :operator ? op : key) match = send("match_#{type}", filter, target, value) else if match_operator(filter, op, value) match = true type = :operator elsif match_key(filter, key) match = true type = :key else match = false end end [match, type] end
@api private
# File lib/mss/core/policy.rb, line 667 def strip_modifiers(operator) opts = {} MODIFIERS.each do |(regex, mod)| ruby_name = Inflection.ruby_name(mod).to_sym opts[ruby_name] = "" if operator.to_s =~ regex opts[ruby_name] = mod operator = operator.to_s.sub(regex, '').to_sym end end [operator, opts] end
@api private
# File lib/mss/core/policy.rb, line 584 def translate_greater_than(example, opts) base_translate(example, "GreaterThan", opts[:equals]) end
@api private
# File lib/mss/core/policy.rb, line 591 def translate_gte(example, opts) translate_greater_than(example, { :equals => "Equals" }) end
@api private
# File lib/mss/core/policy.rb, line 546 def translate_is(example, opts) return "Bool" if type_notation(example) == "Bool" base_translate(example, "Equals", opts[:ignore_case]) end
@api private
# File lib/mss/core/policy.rb, line 609 def translate_is_arn(example, opts) "ArnEquals" end
@api private
# File lib/mss/core/policy.rb, line 621 def translate_is_arn_like(example, opts) "ArnLike" end
@api private
# File lib/mss/core/policy.rb, line 597 def translate_is_ip_address(example, opts) "IpAddress" end
@api private
# File lib/mss/core/policy.rb, line 497 def translate_key(key) if key.kind_of?(Symbol) if key.to_s =~ /^s3_(.*)$/ s3_name = $1 if s3_name == "version_id" or s3_name == "location_constraint" s3_name = Inflection.class_name(s3_name) else s3_name.tr!('_', '-') end "s3:#{s3_name}" else "mss:#{Inflection.class_name(key.to_s)}" end else key end end
@api private
# File lib/mss/core/policy.rb, line 571 def translate_less_than(example, opts) base_translate(example, "LessThan", opts[:equals]) end
@api private
# File lib/mss/core/policy.rb, line 559 def translate_like(example, opts) base_translate(example, "Like") end
@api private
# File lib/mss/core/policy.rb, line 578 def translate_lte(example, opts) translate_less_than(example, { :equals => "Equals" }) end
@api private
# File lib/mss/core/policy.rb, line 553 def translate_not(example, opts) base_translate(example, "NotEquals", opts[:ignore_case]) end
@api private
# File lib/mss/core/policy.rb, line 615 def translate_not_arn(example, opts) "ArnNotEquals" end
@api private
# File lib/mss/core/policy.rb, line 627 def translate_not_arn_like(example, opts) "ArnNotLike" end
@api private
# File lib/mss/core/policy.rb, line 603 def translate_not_ip_address(example, opts) "NotIpAddress" end
@api private
# File lib/mss/core/policy.rb, line 565 def translate_not_like(example, opts) base_translate(example, "NotLike") end
@api private
# File lib/mss/core/policy.rb, line 533 def translate_operator(operator, example_value) return operator if operator.kind_of?(String) original_operator = operator (operator, opts) = strip_modifiers(operator) raise ArgumentError.new("unrecognized operator #{original_operator}") unless respond_to?("translate_#{operator}", true) send("translate_#{operator}", example_value, opts) end
@api private
# File lib/mss/core/policy.rb, line 639 def type_notation(example) case example when String "String" when Numeric "Numeric" when Time, Date "Date" when true, false "Bool" end end
@api private
# File lib/mss/core/policy.rb, line 524 def valid_operator?(operator) translate_operator(operator, "") true rescue ArgumentError => e false end