class RIMS::Password::LDAPSource

to enable LDAP pass-source plug-in, add the entry of rims/passwd/ldap to load_libraries list.

ex.
   load_libraries:
     - rims/passwd/ldap

Public Class Methods

build_from_conf(config) click to toggle source

configuration entries:

  • "ldap_uri"

  • "base_dn"

  • "attribute"

  • "scope"

  • "filter"

  • "search_bind_auth"

    * <tt>"method"</tt>
    * <tt>"username"</tt>
    * <tt>"password"</tt>
  • "search_bind_verification_skip"

# File lib/rims/passwd/ldap.rb, line 169
def build_from_conf(config)
  unless (config.key? 'ldap_uri') then
    raise 'required ldap_uri parameter at LDAP pass-source configuration.'
  end
  ldap_params = parse_uri(config['ldap_uri'])
  ldap_args = []

  for name in [ :host, :port ]
    value = ldap_params.delete(name) or raise "internal error: #{name}"
    ldap_args << value
  end

  for name in [ :base_dn, :attribute ]
    value = ldap_params.delete(name)
    if (config.key? name.to_s) then
      value = config[name.to_s]
    end
    unless (value) then
      raise "required #{name} parameter at LDAP pass-source configuration."
    end
    ldap_args << value
  end

  for name in [ :scope, :filter, :search_bind_verification_skip ]
    if (config.key? name.to_s) then
      ldap_params[name] = config[name.to_s]
    end
  end

  if (config.key? 'search_bind_auth') then
    case (config['search_bind_auth']['method'])
    when 'anonymous'
      auth = { method: :anonymous }
    when 'simple'
      auth = { method: :simple }
      auth[:username] = config['search_bind_auth']['username'] or raise 'required serach bind username at LDAP pass-source configuration.'
      auth[:password] = config['search_bind_auth']['password'] or raise 'required search bind password at LDAP pass-source configuration.'
    else
      raise "unknown or unsupported bind method type: #{config['search_bind_auth'].inspect}"
    end
    ldap_params[:search_bind_auth] = auth
  end

  self.new(*ldap_args, **ldap_params)
end
new(host, port, base_dn, attr, scope: 'sub', filter: nil, search_bind_auth: { :method => :anonymous }, search_bind_verification_skip: false, encryption: false) click to toggle source
# File lib/rims/passwd/ldap.rb, line 15
def initialize(host, port, base_dn, attr, scope: 'sub', filter: nil,
               search_bind_auth: { :method => :anonymous },
               search_bind_verification_skip: false,
               encryption: false)
  @host = host
  @port = port
  @base_dn = base_dn
  @attr = attr
  @scope_src = scope
  @filter_src = filter
  @search_bind_auth = search_bind_auth
  @search_bind_verification_skip = search_bind_verification_skip
  @encryption = encryption
end
parse_uri(uri_string) click to toggle source
# File lib/rims/passwd/ldap.rb, line 134
def parse_uri(uri_string)
  ldap_params = {}

  ldap_uri = URI.parse(uri_string)
  case (ldap_uri)
  when URI::LDAPS
    ldap_params[:encryption] = true
  when URI::LDAP
    # OK
  else
    raise "not a LDAP URI: #{uri_string}"
  end

  ldap_params[:host] = ldap_uri.host || 'localhost'
  ldap_params[:port] = ldap_uri.port or raise "required LDAP port: #{uri_string}"
  ldap_params[:base_dn] = uri_decode(ldap_uri.dn) if (ldap_uri.dn && ! ldap_uri.dn.empty?)
  ldap_params[:attribute] = uri_decode(ldap_uri.attributes) if ldap_uri.attributes
  ldap_params[:scope] = uri_decode(ldap_uri.scope) if ldap_uri.scope
  ldap_params[:filter] = uri_decode(ldap_uri.filter) if ldap_uri.filter

  ldap_params
end
uri_decode(string) click to toggle source
# File lib/rims/passwd/ldap.rb, line 130
def uri_decode(string)
  string.gsub(/%(\h)(\h)/) { [$&[1, 2].hex].pack('C') }.force_encoding(string.encoding)
end

Public Instance Methods

compare_password(username, password) click to toggle source
# File lib/rims/passwd/ldap.rb, line 117
def compare_password(username, password)
  ldap_open{|ldap|
    if (user_dn = search(ldap, username)) then
      if (ldap.bind(method: :simple, username: user_dn, password: password)) then
        true
      else
        false
      end
    end
  }
end
raw_password?() click to toggle source
# File lib/rims/passwd/ldap.rb, line 57
def raw_password?
  false
end
start() click to toggle source
# File lib/rims/passwd/ldap.rb, line 30
def start
  scheme = @encryption ? 'ldaps' : 'ldap'
  @logger.info("LDAP pass-source: #{scheme}://#{@host}:#{@port}/#{@base_dn}?#{@attr}?#{@scope_src}?#{@filter_src}")

  case (@scope_src)
  when 'base'
    @scope = Net::LDAP::SearchScope_BaseObject
  when 'one'
    @scope = Net::LDAP::SearchScope_SingleLevel
  when 'sub'
    @scope = Net::LDAP::SearchScope_WholeSubtree
  else
    raise "unknown ldap search scope: #{@scope_src}"
  end

  if (@filter_src) then
    filter = Net::LDAP::Filter.construct(@filter_src)
    @filter_factory = proc{|username|
      Net::LDAP::Filter.eq(@attr, username) & filter
    }
  else
    @filter_factory = proc{|username|
      Net::LDAP::Filter.eq(@attr, username)
    }
  end
end
user?(username) click to toggle source
# File lib/rims/passwd/ldap.rb, line 107
def user?(username)
  ldap_open{|ldap|
    if (search(ldap, username)) then
      true
    else
      false
    end
  }
end

Private Instance Methods

ldap_open() { |ldap| ... } click to toggle source
# File lib/rims/passwd/ldap.rb, line 61
def ldap_open
  options = { host: @host, port: @port }
  if (@search_bind_verification_skip) then
    options[:auth] = @search_bind_auth
  end
  if (@encryption) then
    options[:encryption] = :simple_tls
  end

  Net::LDAP.open(options) {|ldap|
    unless (@search_bind_verification_skip) then
      # implicit bind of Net::LDAP.open has no error handling.
      # explicit 2nd bind is required to check bind error.
      if (@logger.debug?) then
        auth = @search_bind_auth.dup
        auth.delete(:password)
        @logger.debug("LDAP bind: #{auth.inspect}")
      end
      ldap.bind(@search_bind_auth) or raise "failed to bind to search: #{ldap.get_operation_result}"
      @logger.debug("LDAP bind OK")
    end

    yield(ldap)
  }
end