class DN

rubocop:disable ClassLength Accepts various DN strings and returns a DN object

Attributes

c[RW]
cn[RW]
dc[RW]
delimiter[RW]
dn_string[RW]
l[RW]
o[RW]
original_dn[RW]
ou[RW]
st[RW]
street[RW]
string_order[RW]
transformation[RW]
uid[RW]

Public Class Methods

new(opts = {}) click to toggle source

Initialize the instance

@param opts [Hash] Options hash for new DN instance attribute values @param opts [String] The DN string you want to parse into a DN @param opts User provided logger vs Rails / Logging default logger NOTE: opts defaults to “upcase”; use “to_s” for no change. @param opts [String] String method for changing DN. @param opts [String] Specify a custom delimiter for dn_string NOTE: opts is a last resort config, defaults to RFC4514 spec. @param opts [Array] Specify the order of RDNs for .to_s @return [DN]

# File lib/dnc/dn.rb, line 27
def initialize(opts = {})
  @dn_string = opts[:dn_string]
  raise 'dnc: dn_string parameter is **required**' if dn_string.nil?
  @original_dn    = dn_string
  @logger         = opts[:logger] || logger
  @transformation = opts[:transformation] || 'upcase'
  @string_order   = opts[:string_order] || %w(cn l st o ou c street dc uid)
  @delimiter      = opts[:delimiter] || identify_delimiter
  format_dn
end

Public Instance Methods

logger() click to toggle source

logger method to return Rails logger if defined, else logging logger

# File lib/dnc/dn.rb, line 39
def logger
  unless defined? @logger
    logger = Logging.logger[self]
    @logger = Kernel.const_defined?('Rails') ? Rails.logger : logger
  end
  @logger
end
split_by_delimiter() click to toggle source

Split passed DN by identified delimiter

# File lib/dnc/dn.rb, line 61
def split_by_delimiter
  dn_string.split(delimiter).reject(&:empty?)
end
to_s() click to toggle source

Convert DN object into a string (order follows RFC4514 LDAP specifications)

# File lib/dnc/dn.rb, line 48
def to_s
  return_string = ''
  @string_order.each do |string_name|
    unless send(string_name.to_sym).blank?
      return_string += ',' unless return_string.empty?
      return_string += send("#{string_name}_string".to_sym)
    end
  end

  return_string
end

Private Instance Methods

assign_rdn_as_array(method_name, value) click to toggle source
# File lib/dnc/dn.rb, line 115
def assign_rdn_as_array(method_name, value)
  send("#{method_name}=", Array.wrap(send(method_name)))
  send(method_name).push(value)
end
assign_rdn_as_string(method_name, value) click to toggle source
# File lib/dnc/dn.rb, line 111
def assign_rdn_as_string(method_name, value)
  send("#{method_name}=", value)
end
delimiter_regexp() click to toggle source

Regex to match the DN delimiter by getting the 2nd key non-word predecessor

# File lib/dnc/dn.rb, line 136
def delimiter_regexp
  /\A.*=.*((([^\w\s\+\)\(])|([_]))\s?)\w+=.*\z/
end
dn_array_to_string(getter_method) click to toggle source

Dynamically define a method to return DN array values as string format

# File lib/dnc/dn.rb, line 171
def dn_array_to_string(getter_method)
  tmp_str = ''
  value = send(getter_method.to_sym)
  value.each do |el|
    tmp_str += ',' unless tmp_str.empty?
    tmp_str += "#{getter_method.to_s.upcase}=#{el}"
  end

  tmp_str
end
dn_begins_properly?(dn_str) click to toggle source

Verify DN starts with 'CN='

# File lib/dnc/dn.rb, line 131
def dn_begins_properly?(dn_str)
  dn_str.nil? ? false : /^#{Regexp.escape(delimiter)}?CN/i.match(dn_str)
end
dn_hash_to_string(getter_method) click to toggle source

Dynamically define a method to return DN hash values as string format

# File lib/dnc/dn.rb, line 183
def dn_hash_to_string(getter_method)
  return_string = ''
  value = send(getter_method.to_sym)
  value.each do |key, string|
    return_string += '+' unless return_string.empty?
    return_string += "#{key.upcase}=#{string}"
  end

  return_string
end
dn_string_to_string(getter_method) click to toggle source

Dynamically define a method to return DN string values as string format

# File lib/dnc/dn.rb, line 195
def dn_string_to_string(getter_method)
  "#{getter_method.to_s.upcase}=#{send(getter_method.to_sym)}"
end
dynamic_strings(getter_method, value_class) click to toggle source

Dynamically format the “#{attr}_string” method by value's class type

# File lib/dnc/dn.rb, line 162
def dynamic_strings(getter_method, value_class)
  send("dn_#{value_class.to_s.downcase}_to_string".to_sym, getter_method)
end
format_dn() click to toggle source

Orchestrates reformatting DN to expected element order for LDAP auth.

# File lib/dnc/dn.rb, line 68
def format_dn
  # Transform dn_string for consistency / uniqueness
  @dn_string = dn_string.send(transformation.to_sym)
  format_dn_element_order unless dn_begins_properly?(dn_string)
  parse_rdns_to_attrs
  self
end
format_dn_element_order() click to toggle source

Ensure order of DN elements is proper for CAS server

# File lib/dnc/dn.rb, line 121
def format_dn_element_order
  formatted_dn = split_by_delimiter.reverse.join(delimiter)
  if dn_begins_properly?(formatted_dn)
    @dn_string = formatted_dn
  else
    raise "DN invalid format for LDAP authentication, DN:\r\n#{original_dn}"
  end
end
identify_delimiter() click to toggle source

Identify and set the DN delimiter

# File lib/dnc/dn.rb, line 141
def identify_delimiter
  logger.debug("DN.identify_delimeter: #{dn_string}")
  delimiter_regexp.match(dn_string)[1]
rescue
  raise DnDelimiterUnparsableError, "DN delimiter could not be identified
           \r\nPlease ensure your string complies with RFC1779 formatting."
end
method_missing(method_name) click to toggle source
Calls superclass method
# File lib/dnc/dn.rb, line 149
def method_missing(method_name)
  # Catch methods that end with _string
  method_match = method_name.to_s.match(/(.+)_string\z/)
  unless method_match.blank?
    method = method_match[1]
    method_class = send(method.to_sym).class
    return send(:dynamic_strings, method.to_s, method_class)
  end

  super
end
parse_nested_rdn(rdn) click to toggle source
# File lib/dnc/dn.rb, line 100
def parse_nested_rdn(rdn)
  rdn_keypairs = {}
  rdn_array = rdn.split('+')
  rdn_array.each do |string|
    keypair = string.split('=')
    rdn_keypairs[keypair[0].to_sym] = keypair[1]
  end

  send("#{rdn_keypairs.keys.first.downcase}=", rdn_keypairs)
end
parse_rdns_to_attrs() click to toggle source

Parse @dn_string RDNs and assign them to DN attributes

# File lib/dnc/dn.rb, line 77
def parse_rdns_to_attrs
  split_by_delimiter.each do |rdn|
    if rdn.include?('+')
      parse_nested_rdn(rdn)
    else
      parse_top_level_rdn(rdn)
    end
  end

  self
end
parse_top_level_rdn(rdn) click to toggle source
# File lib/dnc/dn.rb, line 89
def parse_top_level_rdn(rdn)
  rdn_array = rdn.split('=')
  method = rdn_array[0].downcase.to_sym
  value  = rdn_array[1]
  if send(method).blank?
    assign_rdn_as_string(method, value)
  else
    assign_rdn_as_array(method, value)
  end
end