Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/iprange enhancements #252

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/nexpose.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
require 'nexpose/global_settings'
require 'nexpose/group'
require 'nexpose/dag'
require 'nexpose/ip_range'
require 'nexpose/manage'
require 'nexpose/multi_tenant_user'
require 'nexpose/password_policy'
Expand Down
181 changes: 181 additions & 0 deletions lib/nexpose/ip_range.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
module Nexpose
# Object that represents a single IP address or an inclusive range of IP addresses.
# If to is nil then the from field will be used to specify a single IP Address only.
#
class IPRange
# Start of range *Required
attr_accessor :from
# End of range *Optional (If nil then IPRange is a single IP Address)
attr_accessor :to

# @overload initialize(ip)
# @param [#to_s] from the IP single IP address.
# @example
# Nexpose::IPRange.new('192.168.1.0')
#
# @overload initialize(start_ip, end_ip)
# @param [#to_s] from the IP to start the range with.
# @param [#to_s] to the IP to end the range with.
# @example
# Nexpose::IPRange.new('192.168.1.0', '192.168.1.255')
#
# @overload initialize(cidr_range)
# @param [#to_s] from the CIDR notation IP address range.
# @example
# Nexpose::IPRange.new('192.168.1.0/24')
# @note The range will not be stripped of reserved IP addresses (such as
# x.x.x.0 and x.x.x.255).
#
# @return [IPRange] an IP address range of one or more addresses.
def initialize(lower, upper = nil)
range = IPAddr.new(lower).to_range
span = range.last.to_i - range.first.to_i
@to = case upper
when nil, lower
span > 0 ? range.last.to_s : nil
else
IPAddr.new(upper).to_s
end
@from = range.first.to_s
end

# Size of the IP range. The total number of IP addresses represented
# by this range.
#
# @return [Fixnum] size of the range.
#
def size
1 + case @to
when nil
0
else
upper_ip.to_i - lower_ip.to_i
end
end

def single?
(size == 1)
end

include Comparable

def <=>(other)
case other
when Nexpose::IPRange
if other.upper_ip < lower_ip
1
elsif upper_ip < other.lower_ip
-1
else # Overlapping
0
end
else
(addr = coerce_address(other)) ? self.<=>(Nexpose::IPRange.new(addr)) : 1
end
end

def ==(other)
eql?(other)
end

def eql?(other)
return false unless other.respond_to? :from
@from == other.from && @to == other.to
end

IncompatibleType = Class.new(ArgumentError)

#
# @overload include?(other)
# @param other [IPAddr] /32 IP address
#
# @overload include?(IPAddr)
# @param other [IPAddr] CIDR range
#
# @overload include?(other)
# @param other [IPRange] single IP address
#
# @overload include?(other)
# @param other [IPRange] IPRange spanning multiple IPs
#
# @overload include?(other)
# @param other [String] /32 IP address
#
# @overload include?(other)
# @param other [String] CIDR range
#
# @return [FalseClass]|[TrueClass] if other is bounded inclusively by #from and #to
#
def include?(other)
case other
when IPAddr
include_ipaddr?(other)
when Nexpose::IPRange
include_iprange?(other)
when String
other_addr = coerce_address(other)
other_addr ? include_ipaddr?(other_addr) : false
else
raise IncompatibleType, "#{other.class} not IPAddr, Nexpose::IPRange, or String"
end
end

def hash
to_xml.hash
end

def as_xml
xml = REXML::Element.new('range')
xml.add_attributes('from' => @from, 'to' => @to)
xml
end
alias_method :to_xml_elem, :as_xml

def to_xml
as_xml.to_s
end

def to_s
return from.to_s if to.nil?
"#{from} - #{to}"
end

protected

def lower_ip
IPAddr.new(@from)
end

def upper_ip
@to.nil? ? IPAddr.new(@from) : IPAddr.new(@to)
end

private

def coerce_address(str)
addr = begin
IPAddr.new(str)
rescue IPAddr::AddressFamilyError, IPAddr::InvalidAddressError => invalid
warn format('could not coerce "%s" to IPAddr: %s', str, invalid.to_s)
false
end
addr
end

def include_ipaddr?(other)
ip_range = other.to_range
lower = ip_range.first.to_s
upper = ip_range.last.to_s
nxp_iprange = Nexpose::IPRange.new(lower, upper)
include_iprange?(nxp_iprange)
end

def include_iprange?(other)
if single?
other.single? ? eql?(other) : false
else
(lower_ip <= other.lower_ip) && (other.upper_ip <= upper_ip)
end
end
end
end
115 changes: 0 additions & 115 deletions lib/nexpose/site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -639,119 +639,4 @@ def to_s
@host.to_s
end
end

# Object that represents a single IP address or an inclusive range of IP addresses.
# If to is nil then the from field will be used to specify a single IP Address only.
#
class IPRange
# Start of range *Required
attr_accessor :from
# End of range *Optional (If nil then IPRange is a single IP Address)
attr_accessor :to

# @overload initialize(ip)
# @param [#to_s] from the IP single IP address.
# @example
# Nexpose::IPRange.new('192.168.1.0')
#
# @overload initialize(start_ip, end_ip)
# @param [#to_s] from the IP to start the range with.
# @param [#to_s] to the IP to end the range with.
# @example
# Nexpose::IPRange.new('192.168.1.0', '192.168.1.255')
#
# @overload initialize(cidr_range)
# @param [#to_s] from the CIDR notation IP address range.
# @example
# Nexpose::IPRange.new('192.168.1.0/24')
# @note The range will not be stripped of reserved IP addresses (such as
# x.x.x.0 and x.x.x.255).
#
# @return [IPRange] an IP address range of one or more addresses.
def initialize(from, to = nil)
@from = from
@to = to unless from == to

return unless @to.nil?

range = IPAddr.new(@from.to_s).to_range
unless range.one?
@from = range.first.to_s
@to = range.last.to_s
end
end

# Size of the IP range. The total number of IP addresses represented
# by this range.
#
# @return [Fixnum] size of the range.
#
def size
return 1 if @to.nil?
from = IPAddr.new(@from)
to = IPAddr.new(@to)
(from..to).to_a.size
end

include Comparable

def <=>(other)
return 1 unless other.respond_to? :from
from = IPAddr.new(@from)
to = @to.nil? ? from : IPAddr.new(@to)
cf_from = IPAddr.new(other.from)
cf_to = IPAddr.new(other.to.nil? ? other.from : other.to)
if cf_to < from
1
elsif to < cf_from
-1
else # Overlapping
0
end
end

def ==(other)
eql?(other)
end

def eql?(other)
return false unless other.respond_to? :from
@from == other.from && @to == other.to
end

def include?(single_ip)
return false unless single_ip.respond_to? :from
from = IPAddr.new(@from)
to = @to.nil? ? from : IPAddr.new(@to)
other = IPAddr.new(single_ip)

if other < from
false
elsif to < other
false
else
true
end
end

def hash
to_xml.hash
end

def as_xml
xml = REXML::Element.new('range')
xml.add_attributes({ 'from' => @from, 'to' => @to })
xml
end
alias_method :to_xml_elem, :as_xml

def to_xml
as_xml.to_s
end

def to_s
return from.to_s if to.nil?
"#{from.to_s} - #{to.to_s}"
end
end
end
Loading