class EgovUtils::AuthSource

Constants

B32
NETWORK_EXCEPTIONS

Attributes

provider[RW]

Public Class Methods

authenticate(login, password) click to toggle source
# File lib/egov_utils/auth_source.rb, line 25
def self.authenticate(login, password)
  providers.collect{|p| AuthSource.new(p).authenticate(login, password) }.compact.first
end
config() click to toggle source
# File lib/egov_utils/auth_source.rb, line 17
def self.config
  YAML.load_file(Rails.root.join('config', 'config.yml'))['ldap']
end
find_kerberos_user(login) click to toggle source
# File lib/egov_utils/auth_source.rb, line 33
def self.find_kerberos_user(login)
  kerberos_providers.collect{|p| AuthSource.new(p).get_kerberos_user_dn(login) }.compact.first
end
kerberos_providers() click to toggle source
# File lib/egov_utils/auth_source.rb, line 29
def self.kerberos_providers
  config.select{|provider, config| config['kerberos']}.keys
end
new(provider) click to toggle source
# File lib/egov_utils/auth_source.rb, line 39
def initialize(provider)
  require 'net-ldap'
  @provider = provider
  raise "EgovUtils::AuthSource#initialize - Non existing provider (#{provider.to_s})"  unless self.class.providers.include?(provider)
end
providers() click to toggle source
# File lib/egov_utils/auth_source.rb, line 21
def self.providers
  config.keys
end

Public Instance Methods

authenticate(login, password) click to toggle source
# File lib/egov_utils/auth_source.rb, line 87
def authenticate(login, password)
  return nil if login.blank? || password.blank?

  with_timeout do
    attrs = get_user_dn(login, password)
    if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
      Rails.logger.info "AuthSource#authenticate successful for '#{login}' on provider #{provider} - user found" if Rails.logger
      return attrs.except(:dn)
    end
  end
rescue *NETWORK_EXCEPTIONS => e
  raise AuthSourceException.new(e.message)
end
authenticate_dn(dn, password) click to toggle source

Check if a DN (user record) authenticates with the password

# File lib/egov_utils/auth_source.rb, line 120
def authenticate_dn(dn, password)
  if dn.present? && password.present?
    initialize_ldap_con(dn, password).bind
  end
end
base_group_filter() click to toggle source
# File lib/egov_utils/auth_source.rb, line 115
def base_group_filter
  options['active_directory'] ? Net::LDAP::Filter.eq("objectClass", "group") : Net::LDAP::Filter.eq('objectClass', 'groupOfNames')
end
base_user_filter() click to toggle source
# File lib/egov_utils/auth_source.rb, line 111
def base_user_filter
  Net::LDAP::Filter.eq("objectClass", "user") & Net::LDAP::Filter.eq("objectCategory", "person")
end
encryption() click to toggle source
# File lib/egov_utils/auth_source.rb, line 76
def encryption
  case options['method'].to_s
  when 'ssl'
    :simple_tls
  when 'tls'
    :start_tls
  else
    nil
  end
end
get_kerberos_user_dn(login) click to toggle source
# File lib/egov_utils/auth_source.rb, line 101
def get_kerberos_user_dn(login)
  return nil if login.blank?

  with_timeout do
    search_user_dn(login)
  end
rescue *NETWORK_EXCEPTIONS => e
  raise AuthSourceException.new(e.message)
end
group_members(group_dn) click to toggle source
# File lib/egov_utils/auth_source.rb, line 179
def group_members(group_dn)
  ldap_con = initialize_ldap_con(options['bind_dn'], options['password'])
  results = []
  if group_dn
    ldap_con.search(base: options['base'],
                      filter: base_user_filter & Net::LDAP::Filter.ex('memberOf:1.2.840.113556.1.4.1941', group_dn),
                      attributes: user_search_attributes) do |entry|
      attrs = get_user_attributes_from_ldap_entry(entry)
      if attrs
        attrs[:login] = get_attr(entry, options['attributes']['username'])
        results << attrs
      end
    end
  end
  results
end
host() click to toggle source

Get host of ldap controller from options.

  • :host - this just give one host and EgovUtils just asks that host

  • :domain with :resolve_host set to true. Domain should be domain for your ldap users.

    in this configuration ldap controller host is resolved by asking for the <tt>_ldap._tcp.<domain></tt> DNS record and takes first - solve the load balancing of ldap queries.
# File lib/egov_utils/auth_source.rb, line 63
def host
  if options['host']
    options['host']
  elsif options['resolve_host'] && options['domain']
    host_dns.target.to_s
  end
end
host_dns() click to toggle source

Resolves host name - it is used only if option :resolve_host is set to true and expect :domain to be defined as well. ldap controller host is resolved by asking for the _ldap._tcp.<domain> DNS record and takes first - solve the load balancing of ldap queries.

# File lib/egov_utils/auth_source.rb, line 51
def host_dns
  require 'resolv'
  @host_dns = Resolv::DNS.open do |dns|
                dns.getresource('_ldap._tcp.'+options['domain'], Resolv::DNS::Resource::IN::SRV)
              end
end
member?(user_dn, group_dn) click to toggle source
# File lib/egov_utils/auth_source.rb, line 168
def member?(user_dn, group_dn)
  ldap_con = initialize_ldap_con(options['bind_dn'], options['password'])
  Rails.logger.debug("Membership in group (#{group_dn}) for (#{user_dn})")
  ldap_con.search(base: user_dn,
                    filter: base_user_filter & Net::LDAP::Filter.ex('memberOf:1.2.840.113556.1.4.1941', group_dn),
                    attributes: ['dn']) do |entry|
    return true
  end
  return false
end
onthefly_register?() click to toggle source
# File lib/egov_utils/auth_source.rb, line 196
def onthefly_register?
  !!options['onthefly_register']
end
options() click to toggle source
# File lib/egov_utils/auth_source.rb, line 45
def options
  @options ||= self.class.config[provider].dup
end
port() click to toggle source

Returns ldap controller port. If :resolve_host is set to true and option for port is not defined, it uses port from DNS response.

# File lib/egov_utils/auth_source.rb, line 72
def port
  options['resolve_host'] ? (options['port'] || host_dns.port.to_i) : options['port']
end
register_members_only?() click to toggle source
# File lib/egov_utils/auth_source.rb, line 200
def register_members_only?
  options['onthefly_register'] == 'members'
end
search_group(q, by_login=false) click to toggle source
# File lib/egov_utils/auth_source.rb, line 149
def search_group(q, by_login=false)
  q = q.to_s.strip
  return [] unless q.present?

  results = []
  search_filter = base_group_filter & group_search_filters(q)
  ldap_con = initialize_ldap_con(options['bind_dn'], options['password'])
  ldap_con.search(:base => options['base'],
                  :filter => search_filter,
                  :attributes => group_search_attributes,
                  :size => 10) do |entry|
    attrs = get_group_attributes_from_ldap_entry(entry)
    results << attrs if attrs
  end
  results
rescue *NETWORK_EXCEPTIONS => e
  raise AuthSourceException.new(e.message)
end
search_user(q, by_login=false) click to toggle source

Searches the source for users and returns an array of results

# File lib/egov_utils/auth_source.rb, line 127
def search_user(q, by_login=false)
  q = q.to_s.strip
  return [] unless q.present?

  results = []
  search_filter = base_user_filter & user_search_filters(q)
  ldap_con = initialize_ldap_con(options['bind_dn'], options['password'])
  ldap_con.search(:base => options['base'],
                  :filter => search_filter,
                  :attributes => user_search_attributes,
                  :size => 10) do |entry|
    attrs = get_user_attributes_from_ldap_entry(entry)
    if attrs
      attrs[:login] = get_attr(entry, options['attributes']['username'])
      results << attrs
    end
  end
  results
rescue *NETWORK_EXCEPTIONS => e
  raise AuthSourceException.new(e.message)
end

Private Instance Methods

b48_to_fixnum(i16, i32) click to toggle source
# File lib/egov_utils/auth_source.rb, line 355
def b48_to_fixnum(i16, i32)
  i32 + (i16 * B32)
end
get_attr(entry, attr_name) click to toggle source
# File lib/egov_utils/auth_source.rb, line 336
def get_attr(entry, attr_name)
  if attr_name.is_a? Array
    attr_name.collect{|an| get_attr(entry, an).presence }.compact.first.to_s
  elsif !attr_name.blank?
    value = entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
    value.to_s.force_encoding('UTF-8')
  end
end
get_group_attributes_from_ldap_entry(entry) click to toggle source
# File lib/egov_utils/auth_source.rb, line 238
def get_group_attributes_from_ldap_entry(entry)
  {
   :dn => entry.dn,
   :name => get_attr(entry, 'cn'),
   :provider => provider,
   :ldap_uid => get_sid_string( get_attr(entry, 'objectSID') ),
   :external_uid => entry.dn
  }
end
get_group_dn(**options) click to toggle source
# File lib/egov_utils/auth_source.rb, line 287
def get_group_dn(**options)
  ldap_con = initialize_ldap_con(options['bind_dn'], options['password'])
  ldap_con.search(base: options['base'],
                  filter: base_group_filter & ( options[:sid] ? Net::LDAP::Filter.eq('objectSID', options[:sid]) : group_search_filters(options[:name]) ),
                  attributes: ['dn']) do |entry|
    return get_attr(entry, 'dn')
  end
end
get_sid_string(data) click to toggle source

converts hex representation of SID returned by AD to its string representation

# File lib/egov_utils/auth_source.rb, line 346
def get_sid_string(data)
  return if data.nil?
  sid = data.unpack('b x nN V*')
  sid[1, 2] = Array[nil, b48_to_fixnum(sid[1], sid[2])]
  'S-' + sid.compact.join('-')
end
get_user_attributes_from_ldap_entry(entry) click to toggle source
# File lib/egov_utils/auth_source.rb, line 227
def get_user_attributes_from_ldap_entry(entry)
  {
   :dn => entry.dn,
   :login => get_attr(entry, options['attributes']['username']),
   :firstname => get_attr(entry, options['attributes']['first_name']),
   :lastname => get_attr(entry, options['attributes']['last_name']),
   :mail => get_attr(entry, options['attributes']['email']),
   :provider => provider
  }
end
get_user_dn(login, password=nil) click to toggle source
# File lib/egov_utils/auth_source.rb, line 265
def get_user_dn(login, password=nil)
  ldap_con = nil
  if options['bind_dn'].include?("$login")
    ldap_con = initialize_ldap_con(options['bind_dn'].sub("$login", Net::LDAP::DN.escape(login)), password)
  else
    ldap_con = initialize_ldap_con(options['bind_dn'], options['password'])
  end
  attrs = nil
  search_filter = base_user_filter & login_filters(login)
  ldap_con.search( :base => options['base'],
                   :filter => search_filter,
                   :attributes=> user_search_attributes) do |entry|
    if onthefly_register?
      attrs = get_user_attributes_from_ldap_entry(entry)
    else
      attrs = {:dn => entry.dn}
    end
    Rails.logger.debug "DN found for #{login}: #{attrs[:dn]}" if Rails.logger && Rails.logger.debug?
  end
  attrs
end
group_search_attributes() click to toggle source
# File lib/egov_utils/auth_source.rb, line 261
def group_search_attributes
  ['dn', 'cn', 'objectSID']
end
group_search_filters(q) click to toggle source
# File lib/egov_utils/auth_source.rb, line 332
def group_search_filters(q)
  Net::LDAP::Filter.begins('cn', q)
end
initialize_ldap_con(ldap_user, ldap_password) click to toggle source
# File lib/egov_utils/auth_source.rb, line 214
def initialize_ldap_con(ldap_user, ldap_password)
  options = { :host => self.host,
              :port => self.port,
              :encryption => encryption
            }
  unless ldap_user.blank? && ldap_password.blank?
    options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password })
  else
    options.merge!(:auth => { :method => :anonymous })
  end
  Net::LDAP.new options
end
login_attributes() click to toggle source
# File lib/egov_utils/auth_source.rb, line 253
def login_attributes
  if onthefly_register?
    user_search_attributes
  else
    ['dn']
  end
end
login_filters(login) click to toggle source
# File lib/egov_utils/auth_source.rb, line 314
def login_filters(login)
  filters = options['attributes']['username'].collect{|un| Net::LDAP::Filter.eq(un, login)}
  filters[1..-1].inject(filters.first){|filter, lf| filter | lf }
end
login_search_filters(q) click to toggle source
# File lib/egov_utils/auth_source.rb, line 319
def login_search_filters(q)
  filters = options['attributes']['username'].collect{|un| Net::LDAP::Filter.begins(un, q)}
  filters[1..-1].inject(filters.first){|filter, lf| filter | lf }
end
search_user_dn(login, password=nil) click to toggle source
# File lib/egov_utils/auth_source.rb, line 296
def search_user_dn(login, password=nil)
  ldap_con = nil
  if options['bind_dn'].include?("$login")
    ldap_con = initialize_ldap_con(options['bind_dn'].sub("$login", Net::LDAP::DN.escape(login)), password)
  else
    ldap_con = initialize_ldap_con(options['bind_dn'], options['password'])
  end
  attrs = nil
  search_filter = login_search_filters(login) #base_filter & Net::LDAP::Filter.eq(self.attr_login, login)
  ldap_con.search( :base => options['base'],
                   :filter => search_filter,
                   :attributes=> user_search_attributes) do |entry|
    attrs ||= get_user_attributes_from_ldap_entry(entry)
    Rails.logger.debug "DN found for #{login}: #{attrs[:dn]}" if Rails.logger && Rails.logger.debug?
  end
  attrs
end
user_search_attributes() click to toggle source

Return the attributes needed for the LDAP search. It will only include the user attributes if on-the-fly registration is enabled

# File lib/egov_utils/auth_source.rb, line 250
def user_search_attributes
  ['dn'] + options['attributes']['username'] + options['attributes']['email'] + [options['attributes']['name'], options['attributes']['first_name'], options['attributes']['last_name']]
end
user_search_filters(q) click to toggle source
# File lib/egov_utils/auth_source.rb, line 324
def user_search_filters(q)
  Net::LDAP::Filter.begins(options['attributes']['name'], q) |
    Net::LDAP::Filter.begins(options['attributes']['first_name'], q) |
    Net::LDAP::Filter.begins(options['attributes']['last_name'], q) |
    Net::LDAP::Filter.begins(options['attributes']['username'].first, q) |
    Net::LDAP::Filter.begins(options['attributes']['email'].first, q)
end
with_timeout() { || ... } click to toggle source
# File lib/egov_utils/auth_source.rb, line 205
def with_timeout(&block)
  timeout = 20
  Timeout.timeout(timeout) do
    return yield
  end
rescue Timeout::Error => e
  raise AuthSourceTimeoutException.new(e.message)
end