Class: Aker::Ldap::Authority

Inherits:
Object
  • Object
show all
Includes:
Authorities::Support::FindSoleUser
Defined in:
lib/aker/ldap/authority.rb

Overview

A generic authority for performing authentication and user lookup via an LDAP server. It authenticates username/password combinations and fills in demographic information from the LDAP record. It also implements find_users to provide searches separately from authentication.

This authority supports multiple instances if you need to combine the results from multiple LDAP servers in a single Aker configuration. Setting up multiple instances either requires directly constructing the instances (i.e., not using the ‘:ldap` alias and having Aker construct them for you) or writing separately constructable subclasses. The former makes sense for one-off configurations while the latter is better for reuse. See also Configuration::Slice for setting default parameters in extensions and Configuration#alias_authority for giving pithy names to authority subclasses.

Examples:

Configuring a single LDAP authority

Aker.configure {
  ldap_parameters :server => "ldap.example.org"
  authority :ldap
}

Configuring multiple LDAP authorities via manual construction

Aker.configure {
  hr_parameters :server => "hr.example.com", :port => 5003
  dept_parameters :server => "ldap.mydept.example.com"

  hr_ldap = Aker::Ldap::Authority.new(this, :hr)
  dept_ldap = Aker::Ldap::Authority.new(this, :dept)

  authorities hr_ldap, dept_ldap
}

Defining multiple LDAP authorities via subclassing

# Not pictured: using a default slice and aliasing to make
# these authorities look like built-in authorities.

class HrLdap < Aker::Ldap::Authority
  def initialize(config); super config, :hr; end
end
class DeptLdap < Aker::Ldap::Authority
  def initialize(config); super config, :dept; end
end

Aker.configure {
  hr_parameters :server => "hr.example.com", :port => 5003
  dept_parameters :server => "ldap.mydept.example.com"
  authorities HrLdap, DeptLdap
}

Author:

  • Rhett Sutphin

Since:

  • 2.2.0

Constant Summary collapse

DEFAULT_ATTRIBUTE_MAP =

See Also:

Since:

  • 2.2.0

{
  :uid => :username,
  :sn => :last_name,
  :givenname => :first_name,
  :title => :title,
  :mail => :email,
  :telephonenumber => :business_phone,
  :facsimiletelephonenumber => :fax,
}.freeze

Instance Method Summary collapse

Methods included from Authorities::Support::FindSoleUser

#find_user

Constructor Details

#initialize(config, name = :ldap) ⇒ Authority

Create a new instance.

Parameters:

  • config (Configuration, Hash)

    the configuration for this instance. If a hash, the parameters are extracted directly. If a Configuration, the parameters are extracted using parameters_for(name) (where ‘name` is the name parameter to this constructor; default is `:ldap`).

  • name (Symbol) (defaults to: :ldap)

    the name for this authority. If you need to have multiple LDAP authorities in the same configuration, distinguish them by name.

Options Hash (config):

  • :server (String)

    The hostname for the LDAP server (required)

  • :port (Integer) — default: 636

    The port to use to connect to the LDAP server

  • :use_tls (Boolean) — default: true

    Whether the LDAP server uses TLS. Note that if you set this to false, you’ll probably need to change the port as well.

  • :user (String)

    A username to use to bind to the server before searching or authenticating (optional)

  • :password (String)

    The password that goes with :user (optional; required if :user is specified)

  • :attribute_map (Hash<Symbol, Symbol>)

    Extensions and overrides for the LDAP-to-Aker user attribute mapping. See #attribute_map for details.

  • :attribute_processors (Hash<Symbol, #call>)

    See #attribute_processors for details.

  • :criteria_map (Hash<Symbol, Symbol>)

    See #criteria_map for details.

Since:

  • 2.2.0



111
112
113
114
115
116
117
118
119
120
# File 'lib/aker/ldap/authority.rb', line 111

def initialize(config, name=:ldap)
  @config =
    case config
    when Aker::Configuration
      config.parameters_for(name)
    else
      config
    end
  validate_config!
end

Instance Method Details

#attribute_mapHash<Symbol, Symbol>

The mapping between attributes from the LDAP server and User attributes. This mapping is used in two ways:

  • When returning users from the LDAP server, the first value for any mapped attribute is used as the value for that attribute in the user object.

  • Aker user attributes in this map will be translated into LDAP attributes when doing a criteria query with #find_users.

There is a default mapping which will be reasonable for many cases. To extend it, provide the ‘:attribute_map` parameter when constructing this authority.

If this mapping is not reversible (i.e., each value is unique), then the behavior of this authority is not defined. Each value in this map must be a writable attribute on Aker::User.

Examples:

ldap.attribute_map # => { :givenname => :first_name }
ldap.find_user('jmt123')
  # => The givenName attribute in the LDAP record will be
  #    mapped to Aker::User#first_name in the returned user
ldap.find_users(:first_name => 'Jo')
  # => The LDAP server will be queried using givenName=Jo.

Returns:

  • (Hash<Symbol, Symbol>)

    the mapping from LDAP attribute to Aker user attribute.

Since:

  • 2.2.0



192
193
194
# File 'lib/aker/ldap/authority.rb', line 192

def attribute_map
  @attribute_map ||= DEFAULT_ATTRIBUTE_MAP.merge(@config[:attribute_map] || {})
end

#attribute_processorsHash<Symbol, #call>

A set of named procs which will be applied while creating a User from an LDAP entry. The values in the map should be procs. Each proc should accept three arguments:

  • The user being created from the LDAP entry.

  • The full ldap entry. This is a hash-like object that allows you to retrieve LDAP attributes using their names in lower case. Values in the entry may be arrays or scalars. They may not be serializable, so before copying a value out you should be sure to dup it.

  • A proc that allows you to safely extract a single value from the entry. The values returned from this proc are safe to set directly in the user.

If there is an entry in this mapping whose key is the same as a key in #attribute_map, the processor will be used instead of the simple mapping implied by ‘attribute_map`.

Examples:

An example processor

lambda { |user, entry, s|
  user.identifiers[:ssn] = s[:ssn]
}

Returns:

  • (Hash<Symbol, #call>)

Since:

  • 2.2.0



228
229
230
# File 'lib/aker/ldap/authority.rb', line 228

def attribute_processors
  @attribute_processors ||= attribute_map_processors.merge(@config[:attribute_processors] || {})
end

#criteria_mapHash<Symbol,Symbol>

A mapping between attributes from the LDAP server and criteria keys used in #find_users. This mapping will be used when translating criteria hashes into LDAP queries. It is similar to #attribute_map in that way, but there are two differences:

  • The mapping is [criteria key] => [LDAP attribute]. This is the reverse of ‘attribute_map`.

  • The “criteria” in ‘attribute_map` have to be User attribute names. This map does not have that restriction.

If a criterion appears both in this map and ‘attribute_map`, the mapping in this map is used.

Returns:

  • (Hash<Symbol,Symbol>)

Since:

  • 2.2.0



254
255
256
# File 'lib/aker/ldap/authority.rb', line 254

def criteria_map
  @config[:criteria_map] || {}
end

#find_users(*criteria) ⇒ Array<User>

Searches for and returns users matching the given criteria. If the criteria is a ‘String`, it is treated as a username. If it is a `Hash`, the keys are interpreted as User attribute names. Those attributes which are directly mappable to LDAP attributes will be used to build a filtered LDAP query. If the `Hash` contains no keys which are mappable to LDAP attribute names, no query will be performed and an empty array will be returned.

Returns:

See Also:

  • Composite#find_users

Since:

  • 2.2.0



310
311
312
313
314
315
# File 'lib/aker/ldap/authority.rb', line 310

def find_users(*criteria)
  with_ldap do |ldap|
    result = find_by_criteria(ldap, *criteria)
    return result.collect { |r| create_user(r) }
  end
end

#ldap_parametersHash

Returns:

  • (Hash)

Since:

  • 2.2.0



261
262
263
264
265
266
267
268
269
270
271
# File 'lib/aker/ldap/authority.rb', line 261

def ldap_parameters
  {
    :host => server, :port => port,
    :auth => if user
               { :method => :simple, :username => user, :password => password }
             else
               { :method => :anonymous }
             end,
    :encryption => (:simple_tls if use_tls)
  }
end

#passwordString?

The password to use with #user.

Returns:

  • (String, nil)

Since:

  • 2.2.0



152
153
154
# File 'lib/aker/ldap/authority.rb', line 152

def password
  @config[:password]
end

#portInteger

The port to use when connecting to the server. Defaults to 636.

Returns:

  • (Integer)

Since:

  • 2.2.0



138
139
140
# File 'lib/aker/ldap/authority.rb', line 138

def port
  @config[:port] || 636
end

#serverString

The configured server’s hostname or other address.

Returns:

  • (String)

Since:

  • 2.2.0



130
131
132
# File 'lib/aker/ldap/authority.rb', line 130

def server
  @config[:server]
end

#use_tlsBoolean

Whether to use TLS when communicating with the server. TLS is enabled by default.

Returns:

  • (Boolean)

Since:

  • 2.2.0



160
161
162
# File 'lib/aker/ldap/authority.rb', line 160

def use_tls
  @config[:use_tls].nil? ? true : @config[:use_tls]
end

#userString?

The user to bind as before searching or authenticating.

Returns:

  • (String, nil)

Since:

  • 2.2.0



145
146
147
# File 'lib/aker/ldap/authority.rb', line 145

def user
  @config[:user]
end

#valid_credentials?(kind, *credentials) ⇒ User, ...

Verifies a username and password using the configured NU LDAP server. Only supports the ‘:user` credential kind. There must be exactly two credentials.

Returns:

  • (User, nil, :unsupported)

    a complete user record if the credentials are valid, ‘nil` if they aren’t valid, and ‘:unsupported` if the first parameter is anything other than `:user`

Since:

  • 2.2.0



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/aker/ldap/authority.rb', line 282

def valid_credentials?(kind, *credentials)
  return :unsupported unless kind == :user

  username, password = credentials
  return nil unless password && !password.strip.empty?

  with_ldap do |ldap|
    result = find_by_criteria(ldap, :username => username)
    if result.size == 1
      return ldap.authentic?(one_value(result[0], :dn), password) ? create_user(result[0]) : nil
    else
      return nil
    end
  end
end