fog-bouncer 0.0.6
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.
- data/.gitignore +19 -0
- data/.simplecov +3 -0
- data/Gemfile +6 -0
- data/README.md +71 -0
- data/Rakefile +11 -0
- data/bin/fog-bouncer +10 -0
- data/bouncer.jpg +0 -0
- data/fog-bouncer.gemspec +25 -0
- data/lib/fog/bouncer.rb +95 -0
- data/lib/fog/bouncer/cli.rb +23 -0
- data/lib/fog/bouncer/group.rb +140 -0
- data/lib/fog/bouncer/group_manager.rb +75 -0
- data/lib/fog/bouncer/ip_permissions.rb +51 -0
- data/lib/fog/bouncer/protocols.rb +115 -0
- data/lib/fog/bouncer/security.rb +88 -0
- data/lib/fog/bouncer/source.rb +87 -0
- data/lib/fog/bouncer/source_manager.rb +59 -0
- data/lib/fog/bouncer/sources.rb +61 -0
- data/lib/fog/bouncer/version.rb +5 -0
- data/spec/fog/bouncer/group_spec.rb +61 -0
- data/spec/fog/bouncer/protocols_spec.rb +25 -0
- data/spec/fog/bouncer/security_spec.rb +85 -0
- data/spec/fog/bouncer/source_spec.rb +49 -0
- data/spec/fog/bouncer/sources/cidr_spec.rb +9 -0
- data/spec/fog/bouncer_spec.rb +45 -0
- data/spec/helper.rb +28 -0
- data/spec/support/security/private.rb +46 -0
- metadata +179 -0
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            module Fog
         | 
| 2 | 
            +
              module Bouncer
         | 
| 3 | 
            +
                module IPPermissions
         | 
| 4 | 
            +
                  def self.from(protocols, options = {})
         | 
| 5 | 
            +
                    permissions = []
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    protocols.each do |protocol|
         | 
| 8 | 
            +
                      next if (options[:remote_only] && protocol.local?) ||
         | 
| 9 | 
            +
                              (options[:local_only] && protocol.remote?)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                      source = protocol.source
         | 
| 12 | 
            +
                      permission = permissions.find { |permission| permission["IpProtocol"] == protocol.type && permission["FromPort"] == protocol.from && permission["ToPort"] == protocol.to }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                      if permission.nil?
         | 
| 15 | 
            +
                        permission = { "Groups" => [], "IpRanges" => [], "IpProtocol" => protocol.type, "FromPort" => protocol.from, "ToPort" => protocol.to }
         | 
| 16 | 
            +
                        permissions << permission
         | 
| 17 | 
            +
                      end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      if source.is_a?(Fog::Bouncer::Sources::CIDR)
         | 
| 20 | 
            +
                        permission["IpRanges"] << { "CidrIp" => source.range }
         | 
| 21 | 
            +
                      else
         | 
| 22 | 
            +
                        permission["Groups"] << { "UserId" => source.user_id, "GroupName" => source.name }
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    permissions
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def self.to(group, permissions)
         | 
| 30 | 
            +
                    permissions.each do |permission|
         | 
| 31 | 
            +
                      remote_sources = []
         | 
| 32 | 
            +
                      remote_sources = remote_sources | permission["groups"].collect { |group| "#{group["groupName"]}@#{group["userId"]}" }
         | 
| 33 | 
            +
                      remote_sources = remote_sources | permission["ipRanges"].collect { |range| range["cidrIp"] }
         | 
| 34 | 
            +
                      remote_sources.each do |remote_source|
         | 
| 35 | 
            +
                        source = group.sources.find { |s| s.match(remote_source) }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                        if source.nil?
         | 
| 38 | 
            +
                          source = Sources.for(remote_source, group)
         | 
| 39 | 
            +
                          group.sources << source
         | 
| 40 | 
            +
                        end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                        source.remote = true
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                        protocol = source.add_protocol(permission["ipProtocol"], Range.new(permission["fromPort"], permission["toPort"]))
         | 
| 45 | 
            +
                        protocol.remote = true
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
            end
         | 
| @@ -0,0 +1,115 @@ | |
| 1 | 
            +
            module Fog
         | 
| 2 | 
            +
              module Bouncer
         | 
| 3 | 
            +
                class Protocol
         | 
| 4 | 
            +
                  attr_reader :from, :local, :source, :to
         | 
| 5 | 
            +
                  attr_writer :local, :remote
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  def self.range(port)
         | 
| 8 | 
            +
                    if port.is_a?(Range)
         | 
| 9 | 
            +
                      [port.begin, port.end]
         | 
| 10 | 
            +
                    else
         | 
| 11 | 
            +
                      [port, port]
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def initialize(port, source)
         | 
| 16 | 
            +
                    @from, @to = Protocol.range(port)
         | 
| 17 | 
            +
                    @source = source
         | 
| 18 | 
            +
                    validate
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def local
         | 
| 22 | 
            +
                    @local ||= false
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def local?
         | 
| 26 | 
            +
                    !!local
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def match(type, port)
         | 
| 30 | 
            +
                    type.to_s == self.type && Protocol.range(port) == [from, to]
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def remote
         | 
| 34 | 
            +
                    @remote ||= false
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def remote?
         | 
| 38 | 
            +
                    !!remote
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def type
         | 
| 42 | 
            +
                    @type ||= self.class.to_s.gsub("Fog::Bouncer::Protocols::", "").downcase
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  def ==(other)
         | 
| 46 | 
            +
                    type == other.type &&
         | 
| 47 | 
            +
                    from == other.from &&
         | 
| 48 | 
            +
                    to == other.to
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def <=>(other)
         | 
| 52 | 
            +
                    [from, to] <=> [other.from, other.to]
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  def inspect
         | 
| 56 | 
            +
                    "<#{self.class.name} @from=#{from.inspect} @to=#{to.inspect} @local=#{local} @remote=#{remote}>"
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def to_log
         | 
| 60 | 
            +
                    { source: source.source, protocol: type, from: from, to: to }
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                module Protocols
         | 
| 65 | 
            +
                  class InvalidICMPType < StandardError; end
         | 
| 66 | 
            +
                  class InvalidPort < StandardError; end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  class ICMP < Protocol
         | 
| 69 | 
            +
                    ICMP_MAPPING = {
         | 
| 70 | 
            +
                      all: -1,
         | 
| 71 | 
            +
                      ping: 8..0
         | 
| 72 | 
            +
                    }
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    ICMP_TYPE_RANGE = (-1..255)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    def initialize(port, source)
         | 
| 77 | 
            +
                      if port.is_a?(Symbol) && range = ICMP_MAPPING[port]
         | 
| 78 | 
            +
                        port = range
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                      super
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    def match(type, port)
         | 
| 84 | 
            +
                      if port.is_a?(Symbol) && range = ICMP_MAPPING[port]
         | 
| 85 | 
            +
                        type.to_s == self.type && Protocol.range(range) == [from, to]
         | 
| 86 | 
            +
                      else
         | 
| 87 | 
            +
                        super
         | 
| 88 | 
            +
                      end
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    private
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    def validate
         | 
| 94 | 
            +
                      raise InvalidICMPType.new("Must be between and including -1 and 255.") unless ICMP_TYPE_RANGE.include?(from)
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  class TCP < Protocol
         | 
| 99 | 
            +
                    private
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    def validate
         | 
| 102 | 
            +
                      raise InvalidPort.new("Invalid port #{from}. Must be between and including 0 and 65535.") unless (0..65535).include?(from)
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                  class UDP < Protocol
         | 
| 107 | 
            +
                    private
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    def validate
         | 
| 110 | 
            +
                      raise InvalidPort.new("Invalid port #{from}. Must be between and including 0 and 65535.") unless (0..65535).include?(from)
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
              end
         | 
| 115 | 
            +
            end
         | 
| @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            module Fog
         | 
| 2 | 
            +
              module Bouncer
         | 
| 3 | 
            +
                class DefinitionNotFound < StandardError; end
         | 
| 4 | 
            +
                class SourceBlockRequired < StandardError; end
         | 
| 5 | 
            +
                class Security
         | 
| 6 | 
            +
                  attr_reader :name, :description
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(name, &block)
         | 
| 9 | 
            +
                    @name = name
         | 
| 10 | 
            +
                    @definitions = {}
         | 
| 11 | 
            +
                    @using = []
         | 
| 12 | 
            +
                    instance_eval(&block)
         | 
| 13 | 
            +
                    apply_definitions
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def accounts
         | 
| 17 | 
            +
                    @accounts ||= { 'amazon-elb' => 'amazon-elb', 'self' => Fog::Bouncer.aws_account_id }
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def define(name, source, &block)
         | 
| 21 | 
            +
                    raise SourceBlockRequired unless block_given?
         | 
| 22 | 
            +
                    @definitions[name] = { source: source, block: block }
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def definitions(name)
         | 
| 26 | 
            +
                    @definitions[name] || raise(DefinitionNotFound.new("No definition found for #{name}."))
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def extra_remote_groups
         | 
| 30 | 
            +
                    groups.select { |group| !group.local? && group.remote? }
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def groups
         | 
| 34 | 
            +
                    @groups ||= []
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def import_remote_groups
         | 
| 38 | 
            +
                    Fog::Bouncer.fog.security_groups.each do |remote_group|
         | 
| 39 | 
            +
                      group = group(remote_group.name, remote_group.description)
         | 
| 40 | 
            +
                      group.remote = remote_group
         | 
| 41 | 
            +
                      IPPermissions.to(group, remote_group.ip_permissions) if remote_group.ip_permissions
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  def missing_remote_groups
         | 
| 46 | 
            +
                    groups.select { |group| group.local? && !group.remote? }
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def sync
         | 
| 50 | 
            +
                    GroupManager.new(self).synchronize
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  def use(name)
         | 
| 54 | 
            +
                    @using << definitions(name)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def clear_remote
         | 
| 58 | 
            +
                    GroupManager.new(self).clear
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  private
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def account(name, account_id)
         | 
| 64 | 
            +
                    accounts[name] = account_id
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  def apply_definitions
         | 
| 68 | 
            +
                    return if @using.empty?
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    @using.each do |definition|
         | 
| 71 | 
            +
                      @groups.each do |group|
         | 
| 72 | 
            +
                        group.add_source(definition[:source], &definition[:block])
         | 
| 73 | 
            +
                      end
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  def group(name, description, &block)
         | 
| 78 | 
            +
                    group = groups.find { |group| group.name == name }
         | 
| 79 | 
            +
                    if group.nil?
         | 
| 80 | 
            +
                      group = Group.new(name, description, self, &block)
         | 
| 81 | 
            +
                      groups << group
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    group
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
            end
         | 
| @@ -0,0 +1,87 @@ | |
| 1 | 
            +
            module Fog
         | 
| 2 | 
            +
              module Bouncer
         | 
| 3 | 
            +
                class Source
         | 
| 4 | 
            +
                  attr_reader :group, :source
         | 
| 5 | 
            +
                  attr_writer :local, :remote
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  def initialize(source, group, &block)
         | 
| 8 | 
            +
                    @source = source
         | 
| 9 | 
            +
                    @group = group
         | 
| 10 | 
            +
                    if block_given?
         | 
| 11 | 
            +
                      @local = true
         | 
| 12 | 
            +
                      instance_eval(&block)
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def extras
         | 
| 17 | 
            +
                    protocols.select { |protocol| !protocol.local? }
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def add_protocol(type, port)
         | 
| 21 | 
            +
                    protocol = protocols.find { |p| p.match(type, port) }
         | 
| 22 | 
            +
                    if protocol.nil?
         | 
| 23 | 
            +
                      protocol = case type.to_sym
         | 
| 24 | 
            +
                      when :icmp
         | 
| 25 | 
            +
                        Fog::Bouncer::Protocols::ICMP.new(port, self)
         | 
| 26 | 
            +
                      when :tcp
         | 
| 27 | 
            +
                        Fog::Bouncer::Protocols::TCP.new(port, self)
         | 
| 28 | 
            +
                      when :udp
         | 
| 29 | 
            +
                        Fog::Bouncer::Protocols::UDP.new(port, self)
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      protocols << protocol
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    protocol
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def local
         | 
| 39 | 
            +
                    @local ||= false
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def local?
         | 
| 43 | 
            +
                    !!local
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  def missing
         | 
| 47 | 
            +
                    protocols.select { |protocol| protocol.local? && !protocol.remote? }
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  def protocols
         | 
| 51 | 
            +
                    @protocols ||= []
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def remote
         | 
| 55 | 
            +
                    @remote ||= false
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def remote?
         | 
| 59 | 
            +
                    !!remote
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  def ==(other)
         | 
| 63 | 
            +
                    source == other.source &&
         | 
| 64 | 
            +
                    group == other.group &&
         | 
| 65 | 
            +
                    protocols.sort! == other.protocols.sort!
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  def inspect
         | 
| 69 | 
            +
                    "<#{self.class.name} @source=#{source.inspect} @local=#{local} @remote=#{remote} @protocols=#{protocols.inspect}>"
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  private
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  def icmp(*ports)
         | 
| 75 | 
            +
                    ports.each { |port| p = add_protocol(:icmp, port); p.local = true }
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  def tcp(*ports)
         | 
| 79 | 
            +
                    ports.each { |port| p = add_protocol(:tcp, port); p.local = true }
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def udp(*ports)
         | 
| 83 | 
            +
                    ports.each { |port| p = add_protocol(:udp, port); p.local = true }
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
            end
         | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            module Fog
         | 
| 2 | 
            +
              module Bouncer
         | 
| 3 | 
            +
                class SourceManager
         | 
| 4 | 
            +
                  def self.log(data, &block)
         | 
| 5 | 
            +
                    Fog::Bouncer.log({source_manager: true}.merge(data), &block)
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def log(data, &block)
         | 
| 9 | 
            +
                    self.class.log({group_name: @group.name}.merge(data), &block)
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def initialize(group)
         | 
| 13 | 
            +
                    @group = group
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def synchronize
         | 
| 17 | 
            +
                    log(synchronize: true) do
         | 
| 18 | 
            +
                      create_missing_source_permissions
         | 
| 19 | 
            +
                      remove_extra_source_permissions
         | 
| 20 | 
            +
                      @group.sources.each { |s| s.remote = true } unless Fog::Bouncer.pretending?
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def create_missing_source_permissions
         | 
| 27 | 
            +
                    if missing_source_permissions.any?
         | 
| 28 | 
            +
                      @group.remote.connection.authorize_security_group_ingress(@group.name, "IpPermissions" => IPPermissions.from(missing_source_permissions, :local_only => true)) unless Fog::Bouncer.pretending?
         | 
| 29 | 
            +
                      missing_source_permissions.each do |protocol|
         | 
| 30 | 
            +
                        log({authorized: true}.merge(protocol.to_log))
         | 
| 31 | 
            +
                        protocol.remote = true unless Fog::Bouncer.pretending?
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def missing_source_permissions
         | 
| 37 | 
            +
                    @group.sources.map do |source|
         | 
| 38 | 
            +
                      source.protocols.select { |p| p.local? && !p.remote? }
         | 
| 39 | 
            +
                    end.flatten.compact
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def remove_extra_source_permissions
         | 
| 43 | 
            +
                    if extra_source_permissions.any?
         | 
| 44 | 
            +
                      @group.remote.connection.revoke_security_group_ingress(@group.name, "IpPermissions" => IPPermissions.from(extra_source_permissions, :remote_only => true)) unless Fog::Bouncer.pretending?
         | 
| 45 | 
            +
                      extra_source_permissions.each do |protocol|
         | 
| 46 | 
            +
                        log({revoked: true}.merge(protocol.to_log))
         | 
| 47 | 
            +
                        protocol.source.protocols.delete_if { |p| p == protocol } unless Fog::Bouncer.pretending?
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def extra_source_permissions
         | 
| 53 | 
            +
                    @group.sources.map do |source|
         | 
| 54 | 
            +
                      source.protocols.select { |p| !p.local? && p.remote? }
         | 
| 55 | 
            +
                    end.flatten.compact
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            require "fog/bouncer/source"
         | 
| 2 | 
            +
            require "ipaddress"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Fog
         | 
| 5 | 
            +
              module Bouncer
         | 
| 6 | 
            +
                module Sources
         | 
| 7 | 
            +
                  def self.for(source, group, &block)
         | 
| 8 | 
            +
                    if source =~ /^\d+\.\d+\.\d+.\d+\/\d+$/
         | 
| 9 | 
            +
                      CIDR.new(source, group, &block)
         | 
| 10 | 
            +
                    else
         | 
| 11 | 
            +
                      Group.new(source, group, &block)
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  class CIDR < Fog::Bouncer::Source
         | 
| 16 | 
            +
                    def initialize(source, group, &block)
         | 
| 17 | 
            +
                      source = IPAddress::IPv4.new(source).to_string
         | 
| 18 | 
            +
                      super
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    def match(source)
         | 
| 22 | 
            +
                      range == source
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    def range
         | 
| 26 | 
            +
                      @source
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  class Group < Fog::Bouncer::Source
         | 
| 31 | 
            +
                    attr_reader :name, :user_alias, :user_id
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def initialize(source, group, &block)
         | 
| 34 | 
            +
                      super
         | 
| 35 | 
            +
                      case source
         | 
| 36 | 
            +
                      when /^(.+)@(.+)$/
         | 
| 37 | 
            +
                        @name = $1
         | 
| 38 | 
            +
                        @user_alias = $2
         | 
| 39 | 
            +
                        if @user_alias[/^\d+$/]
         | 
| 40 | 
            +
                          @user_id = @user_alias
         | 
| 41 | 
            +
                          if account = group.security.accounts.find { |key, id| id == @user_id }
         | 
| 42 | 
            +
                            @user_alias = account[0]
         | 
| 43 | 
            +
                          end
         | 
| 44 | 
            +
                        end
         | 
| 45 | 
            +
                      else
         | 
| 46 | 
            +
                        @name = source
         | 
| 47 | 
            +
                        @user_alias = 'self'
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    def match(source)
         | 
| 52 | 
            +
                      "#{name}@#{user_id}" == source || name == source
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    def user_id
         | 
| 56 | 
            +
                      @user_id ||= group.security.accounts[user_alias]
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         |