Skip to content

Instantly share code, notes, and snippets.

@rbirnie
Last active December 9, 2015 23:59

Revisions

  1. rbirnie revised this gist Jan 10, 2013. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions auth_source_ldap.rb
    Original file line number Diff line number Diff line change
    @@ -143,8 +143,10 @@ def search_for_user_entries(login, password)
    :filter => object_filter & login_filter,
    # only ask for the DN if on-the-fly registration is disabled
    :attributes => required_ldap_attributes.values)
    logger.warn "Search Result: #{ldap_con.get_operation_result.code}"
    logger.warn "Search Message: #{ldap_con.get_operation_result.message}"
    unless ldap_con.get_operation_result.code == 0
    logger.warn "Search Result: #{ldap_con.get_operation_result.code}"
    logger.warn "Search Message: #{ldap_con.get_operation_result.message}"
    end

    # we really care about one match, using the last one, hoping there is only one match :)
    entries ? entries.last : nil
  2. rbirnie revised this gist Jan 10, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion auth_source_ldap.rb
    Original file line number Diff line number Diff line change
    @@ -116,7 +116,7 @@ def effective_user(login)
    end

    def required_ldap_attributes
    return {} unless onthefly_register?
    return {:dn => :dn} unless onthefly_register?
    { :firstname => attr_firstname,
    :lastname => attr_lastname,
    :mail => attr_mail,
  3. rbirnie revised this gist Dec 20, 2012. 1 changed file with 8 additions and 2 deletions.
    10 changes: 8 additions & 2 deletions auth_source_ldap.rb
    Original file line number Diff line number Diff line change
    @@ -42,14 +42,20 @@ def authenticate(login, password)
    logger.debug "LDAP-Auth with User #{effective_user(login)}"
    # first, search for User Entries in LDAP
    entry = search_for_user_entries(login, password)
    return nil unless entry.is_a?(Net::LDAP::Entry)
    unless entry.is_a?(Net::LDAP::Entry)
    logger.warn "not ldap result"
    return nil
    end

    # extract required attributes
    attrs = required_attributes_values(entry)

    # not sure if there is a case were search result without a DN
    # but just to be on the safe side.
    return nil if (dn=attrs.delete(:dn)).empty?
    if (dn=attrs.delete(:dn)).empty?
    logger.warn "no DN"
    return nil
    end

    logger.debug "DN found for #{login}: #{dn}"

  4. rbirnie revised this gist Dec 20, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions auth_source_ldap.rb
    Original file line number Diff line number Diff line change
    @@ -137,8 +137,8 @@ def search_for_user_entries(login, password)
    :filter => object_filter & login_filter,
    # only ask for the DN if on-the-fly registration is disabled
    :attributes => required_ldap_attributes.values)
    logger.warn "Result: #{ldap_con.get_operation_result.code}"
    logger.warn "Message: #{ldap_con.get_operation_result.message}"
    logger.warn "Search Result: #{ldap_con.get_operation_result.code}"
    logger.warn "Search Message: #{ldap_con.get_operation_result.message}"

    # we really care about one match, using the last one, hoping there is only one match :)
    entries ? entries.last : nil
  5. rbirnie revised this gist Dec 20, 2012. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion auth_source_ldap.rb
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,6 @@
    # logger.debug "Result: #{ldap_con.get_operation_result.code}" if logger && logger.debug?
    # logger.debug "Message: #{ldap_con.get_operation_result.message}" if logger && logger.debug?

    #
    # redMine - project management software
    # Copyright (C) 2006 Jean-Philippe Lang
    #
    @@ -138,6 +137,8 @@ def search_for_user_entries(login, password)
    :filter => object_filter & login_filter,
    # only ask for the DN if on-the-fly registration is disabled
    :attributes => required_ldap_attributes.values)
    logger.warn "Result: #{ldap_con.get_operation_result.code}"
    logger.warn "Message: #{ldap_con.get_operation_result.message}"

    # we really care about one match, using the last one, hoping there is only one match :)
    entries ? entries.last : nil
  6. rbirnie revised this gist Dec 20, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions auth_source_ldap.rb
    Original file line number Diff line number Diff line change
    @@ -57,8 +57,8 @@ def authenticate(login, password)
    # finally, authenticate user
    ldap_con = initialize_ldap_con(dn, password)
    unless ldap_con.bind
    logger.warn "Result: #{ldap_con.get_operation_result.code}" if logger && logger.debug?
    logger.warn "Message: #{ldap_con.get_operation_result.message}" if logger && logger.debug?
    logger.warn "Result: #{ldap_con.get_operation_result.code}"
    logger.warn "Message: #{ldap_con.get_operation_result.message}"
    logger.warn "Failed to authenticate #{login}"
    return nil
    end
  7. rbirnie renamed this gist Dec 20, 2012. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion gistfile1.txt → auth_source_ldap.rb
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,8 @@
    # adding these to 56& 57
    # adding these to 60 & 61
    # logger.debug "Result: #{ldap_con.get_operation_result.code}" if logger && logger.debug?
    # logger.debug "Message: #{ldap_con.get_operation_result.message}" if logger && logger.debug?

    #
    # redMine - project management software
    # Copyright (C) 2006 Jean-Philippe Lang
    #
  8. rbirnie created this gist Dec 20, 2012.
    144 changes: 144 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    # adding these to 56& 57
    # logger.debug "Result: #{ldap_con.get_operation_result.code}" if logger && logger.debug?
    # logger.debug "Message: #{ldap_con.get_operation_result.message}" if logger && logger.debug?
    # redMine - project management software
    # Copyright (C) 2006 Jean-Philippe Lang
    #
    # This program is free software; you can redistribute it and/or
    # modify it under the terms of the GNU General Public License
    # as published by the Free Software Foundation; either version 2
    # of the License, or (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program; if not, write to the Free Software
    # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    require 'net/ldap'
    require 'iconv'

    class AuthSourceLdap < AuthSource
    validates_presence_of :host, :port
    validates_presence_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :if => Proc.new { |auth| auth.onthefly_register? }
    validates_length_of :name, :host, :account_password, :maximum => 60, :allow_nil => true
    validates_length_of :account, :base_dn, :maximum => 255, :allow_nil => true
    validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
    validates_numericality_of :port, :only_integer => true

    before_validation :strip_ldap_attributes
    after_initialize :set_defaults

    # Loads the LDAP info for a user and authenticates the user with their password
    # Returns : Array of Strings.
    # Either the users's DN or the user's full details OR nil
    def authenticate(login, password)
    return nil if login.blank? || password.blank?

    logger.debug "LDAP-Auth with User #{effective_user(login)}"
    # first, search for User Entries in LDAP
    entry = search_for_user_entries(login, password)
    return nil unless entry.is_a?(Net::LDAP::Entry)

    # extract required attributes
    attrs = required_attributes_values(entry)

    # not sure if there is a case were search result without a DN
    # but just to be on the safe side.
    return nil if (dn=attrs.delete(:dn)).empty?

    logger.debug "DN found for #{login}: #{dn}"

    # finally, authenticate user
    ldap_con = initialize_ldap_con(dn, password)
    unless ldap_con.bind
    logger.warn "Result: #{ldap_con.get_operation_result.code}" if logger && logger.debug?
    logger.warn "Message: #{ldap_con.get_operation_result.message}" if logger && logger.debug?
    logger.warn "Failed to authenticate #{login}"
    return nil
    end
    # return user's attributes
    attrs
    rescue Net::LDAP::LdapError => text
    raise "LdapError: " + text
    end

    # test the connection to the LDAP
    def test_connection
    ldap_con = initialize_ldap_con(self.account, self.account_password)
    ldap_con.open { }
    rescue Net::LDAP::LdapError => text
    raise "LdapError: " + text
    end

    def auth_method_name
    "LDAP"
    end

    private

    def strip_ldap_attributes
    [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
    write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
    end
    end

    def initialize_ldap_con(ldap_user, ldap_password)
    options = { :host => host,
    :port => port,
    :encryption => (tls ? :simple_tls : nil)
    }
    options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
    Net::LDAP.new options
    end

    def set_defaults
    self.port ||= 389
    end

    def use_user_login_for_auth?
    # returns true if account is defined and includes "$login"
    (account and account.include? "$login")
    end

    def effective_user(login)
    use_user_login_for_auth? ? account.sub("$login", login) : account
    end

    def required_ldap_attributes
    return {} unless onthefly_register?
    { :firstname => attr_firstname,
    :lastname => attr_lastname,
    :mail => attr_mail,
    :dn => :dn,
    }
    end

    def required_attributes_values entry
    Hash[required_ldap_attributes.map do |name, value|
    value = entry[value].is_a?(Array) ? entry[value].first : entry[value]
    [name, value.to_s]
    end]
    end

    def search_for_user_entries(login, password)
    user = effective_user(login)
    pass = use_user_login_for_auth? ? password : account_password
    ldap_con = initialize_ldap_con(user, pass)
    login_filter = Net::LDAP::Filter.eq(attr_login, login)
    object_filter = Net::LDAP::Filter.eq("objectClass", "*")

    # search for a match for our authenticating user.
    entries = ldap_con.search(:base => base_dn,
    :filter => object_filter & login_filter,
    # only ask for the DN if on-the-fly registration is disabled
    :attributes => required_ldap_attributes.values)

    # we really care about one match, using the last one, hoping there is only one match :)
    entries ? entries.last : nil
    end

    end