class Devise::LDAP::Connection

Attributes

ldap[R]
login[R]

Public Class Methods

new(params = {}) click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 9
def initialize(params = {})

  # Option scope determines the mapping_file to use
  @scope = params[:scope]
  config_file_path = "#{Rails.root}/config/ldap/#{@scope}.yml"

  # Read the config file depending on the scope
  ldap_config = YAML.load(ERB.new(File.read(config_file_path)).result)

  # Alter the ssl option in the configuration
  ldap_config[Rails.env]["ssl"] = :simple_tls if ldap_config[Rails.env]["ssl"] === true

  # Determines if logging happens
  @logging_enabled = ldap_config['logging_enabled']

  # Evaluated into Proc objects
  @auth_username_builder  = eval ldap_config['auth_username_builder']
  @auth_password_builder  = eval ldap_config['auth_password_builder']

  # Attributes and groups required for authorisation of an account
  @group_base = ldap_config["group_base"]
  @required_groups = ldap_config["required_groups"]
  @group_membership_attribute = ldap_config.has_key?("group_membership_attribute") ? ldap_config["group_membership_attribute"] : "uniqueMember"
  @required_attributes = ldap_config["require_attribute"]
  @required_attributes_presence = ldap_config["require_attribute_presence"]

  # Various Flags
  @allow_unauthenticated_bind = ["allow_unauthenticated_bind"]
  @check_attributes = ldap_config['check_attributes']
  @check_attributes_presence = ldap_config['check_attributes_presence']
  @use_admin_to_bind = ldap_config['use_admin_to_bind']
  @check_group_membership = ldap_config["check_group_membership"]
  @check_group_membership_without_admin = ldap_config["check_group_membership_without_admin"]
  @update_passwords = ldap_config['update_passwords']
  @create_user = ldap_config['create_user']

  # Other params referencing credentails not part of the config file
  @login = params[:login]
  @password = params[:password]
  @new_password = params[:new_password]

  # Build up the options used to establish an LDAP connection
  ldap_options = {}
  ldap_options[:login] = params[:login] if params[:login]
  ldap_options[:password] =  params[:password] if params[:password]
  ldap_options[:new_password] =  params[:new_password] if params[:new_password]
  ldap_options[:ldap_auth_username_builder] = @auth_username_builder 
  ldap_options[:admin] = @use_admin_to_bind
  ldap_options[:encryption] = ldap_config[Rails.env]["ssl"].to_sym if ldap_config[Rails.env]["ssl"]

  # LDAP server to connect to depending on the current Rails environment in use
  @ldap = Net::LDAP.new(ldap_options)
  @ldap.host = ldap_config[Rails.env]["host"]
  @ldap.port = ldap_config[Rails.env]["port"]
  @ldap.base = ldap_config[Rails.env]["base"]
  @attribute = ldap_config[Rails.env]["attribute"]

  # Admin credentials to use if an admin is set to bind to LDAP (Rails environment dependent)
  @ldap.auth ldap_config[Rails.env]["admin_user"], ldap_config[Rails.env]["admin_password"] if @use_admin_to_bind

end

Private Class Methods

admin() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 288
def self.admin
  ldap = Connection.new(:admin => true).ldap
  unless ldap.bind
    log("Cannot bind to admin LDAP user") 
    raise DeviseLdapAuthenticatable::LdapException, "Cannot connect to admin LDAP user" 
  end

  return ldap
end

Public Instance Methods

authenticate!() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 126
def authenticate!
  return false unless (@password.present? || @allow_unauthenticated_bind)
  @ldap.auth(dn, @password)
  @ldap.bind
end
authenticated?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 132
def authenticated?
  authenticate!
end
authorized?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 144
def authorized?
  log("Authorizing user #{dn}") 
  if !authenticated?
    if last_message_bad_credentials?
      log("Not authorized because of invalid credentials.") 
    elsif last_message_expired_credentials?
      log("Not authorized because of expired credentials.") 
    else
      log("Not authorized because not authenticated.") 
    end
    return false
  elsif !in_required_groups?
    log("Not authorized because not in required groups.") 
    return false
  elsif !has_required_attribute?
    log("Not authorized because does not have required attribute.") 
    return false
  elsif !has_required_attribute_presence?
    log("Not authorized because does not have required attribute present.") 
    return false
  else
    return true
  end
end
change_password!() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 174
def change_password!
  update_ldap(:userPassword => @auth_password_builder .call(@new_password))
end
delete_param(param) click to toggle source

Deletes a parameter from LDAP for an account

# File lib/devise_ldap_multiple/ldap/connection.rb, line 85
def delete_param(param)
  update_ldap [[:delete, param.to_sym, nil]]
end
dn() click to toggle source

Returns the DistinguishedName for an account, regardless of if it exists or not (calls a proc to determine the dn if it doesn’t exists - proc defined in the config file)

# File lib/devise_ldap_multiple/ldap/connection.rb, line 95
def dn
  @dn ||= begin
    log("LDAP dn lookup: #{@attribute}=#{@login}") 
    log("LDAP dn lookup asdf: #{@attribute}=#{@login}") 
    ldap_entry = search_for_login
    if ldap_entry.nil?
      @auth_username_builder .call(@attribute,@login,@ldap)
    else
      ldap_entry.dn
    end
  end
end
expired_valid_credentials?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 169
def expired_valid_credentials?
  log("Authorizing user #{dn}")  
  !authenticated? && last_message_expired_credentials?
end
has_required_attribute?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 226
def has_required_attribute?
  return true unless @check_attributes
  admin_ldap = Connection.admin
  user = find_ldap_user(admin_ldap)
  @required_attributes.each do |key,val|
    matching_attributes = user[key] & Array(val)
    unless (matching_attributes).any?
      log("User #{dn} did not match attribute #{key}:#{val}") 
      return false
    end
  end
  return true
end
has_required_attribute_presence?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 240
def has_required_attribute_presence?
  return true unless @check_attributes_presence
  user = search_for_login
  @required_attributes_presence.each do |key,val|
    if val && !user.attribute_names.include?(key.to_sym)
      log("User #{dn} doesn't include attribute #{key}") 
      return false
    elsif !val && user.attribute_names.include?(key.to_sym)
      log("User #{dn} includes attribute #{key}") 
      return false
    end
  end
  return true
end
in_group?(group_name, group_attribute = LDAP::DEFAULT_GROUP_UNIQUE_MEMBER_LIST_KEY) click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 192
def in_group?(group_name, group_attribute = LDAP::DEFAULT_GROUP_UNIQUE_MEMBER_LIST_KEY)
  in_group = false
  if @check_group_membership_without_admin
    group_checking_ldap = @ldap
  else
    group_checking_ldap = Connection.admin
  end
  unless @ldap_check_group_membership
    group_checking_ldap.search(:base => group_name, :scope => Net::LDAP::SearchScope_BaseObject) do |entry|
      if entry[group_attribute].include? dn
        in_group = true
        log("User #{dn} IS included in group: #{group_name}") 
      end
    end
  else
    # AD optimization - extension will recursively check sub-groups with one query
    # "(memberof:1.2.840.113556.1.4.1941:=group_name)"
    search_result = group_checking_ldap.search(:base => dn,
                      :filter => Net::LDAP::Filter.ex("memberof:1.2.840.113556.1.4.1941", group_name),
                      :scope => Net::LDAP::SearchScope_BaseObject)
    # Will return  the user entry if belongs to group otherwise nothing
    if search_result.length == 1 && search_result[0].dn.eql?(dn)
      in_group = true
      log("User #{dn} IS included in group: #{group_name}") 
    end
  end

  unless in_group
    log("User #{dn} is not in group: #{group_name}") 
  end

  return in_group
end
in_required_groups?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 178
def in_required_groups?
  return true unless @check_group_membership || @check_group_membership_without_admin
  ## FIXME set errors here, the ldap.yml isn't set properly.
  return false if @required_groups.nil?
  for group in @required_groups
    if group.is_a?(Array)
      return false unless in_group?(group[1], group[0])
    else
      return false unless in_group?(group)
    end
  end
  return true
end
last_message_bad_credentials?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 136
def last_message_bad_credentials?
  @ldap.get_operation_result.error_message.to_s.include? 'AcceptSecurityContext error, data 52e'
end
last_message_expired_credentials?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 140
def last_message_expired_credentials?
  @ldap.get_operation_result.error_message.to_s.include? 'AcceptSecurityContext error, data 773'
end
ldap_param_value(param) click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 108
def ldap_param_value(param)
  ldap_entry = search_for_login

  if ldap_entry
    unless ldap_entry[param].empty?
      value = ldap_entry.send(param)
      log("Requested param #{param} has value #{value}") 
      value
    else
      log("Requested param #{param} does not exist") 
      value = nil
    end
  else
    log("Requested ldap entry does not exist") 
    value = nil
  end
end
log(message) click to toggle source

Logs a message to the console and the environment log file

# File lib/devise_ldap_multiple/ldap/connection.rb, line 72
def log (message)
  DeviseLdapMultiple::Logger.send(message) if @logging_enabled
end
password_updatable?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 76
def password_updatable?
  @update_passwords
end
search_for_login() click to toggle source

Searches the LDAP for the login

@return [Object] the LDAP entry found; nil if not found

# File lib/devise_ldap_multiple/ldap/connection.rb, line 268
def search_for_login
  @login_ldap_entry ||= begin
    log("LDAP search for login: #{@attribute}=#{@login}") 
    log("Attribute: #{@attribute.to_s}, Login: #{@login.to_s}")
    log("Scope is: #{@scope}")
    filter = Net::LDAP::Filter.eq(@attribute.to_s, @login.to_s)
    ldap_entry = nil
    match_count = 0
    @ldap.search(:filter => filter) { |entry| ldap_entry = entry; match_count+=1}
    op_result= @ldap.get_operation_result
    if op_result.code!=0 then
      log("LDAP Error #{op_result.code}: #{op_result.message}") 
    end
    log("LDAP search yielded #{match_count} matches") 
    ldap_entry
  end
end
set_param(param, new_value, is_password = false) click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 89
def set_param(param, new_value, is_password = false)
  new_value = @auth_password_builder .call(new_value) if is_password
  update_ldap( { param.to_sym => new_value } )
end
user_creatable?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 80
def user_creatable?
  @create_user
end
user_groups() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 255
def user_groups
  admin_ldap = Connection.admin
  log("Getting groups for #{dn}") 
  filter = Net::LDAP::Filter.eq(@group_membership_attribute, dn)
  admin_ldap.search(:filter => filter, :base => @group_base).collect(&:dn)
end
valid_login?() click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 261
def valid_login?
  !search_for_login.nil?
end

Private Instance Methods

find_ldap_user(ldap) click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 298
def find_ldap_user(ldap)
  log("Finding user: #{dn}") 
  ldap.search(:base => dn, :scope => Net::LDAP::SearchScope_BaseObject).try(:first)
end
update_ldap(ops) click to toggle source
# File lib/devise_ldap_multiple/ldap/connection.rb, line 303
def update_ldap(ops)
  operations = []
  if ops.is_a? Hash
    ops.each do |key,value|
      operations << [:replace,key,value]
    end
  elsif ops.is_a? Array
    operations = ops
  end
  if @use_admin_to_bind
    privileged_ldap = Connection.admin
  else
    authenticate!
    privileged_ldap = self.ldap
  end
  log("Modifying user #{dn}") 
  privileged_ldap.modify(:dn => dn, :operations => operations)
end