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)

  • :search_domain (String)

    The search base to use when looking up users. E.g., ‘“dc=example,dc=org”`. (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



115
116
117
118
119
120
121
122
123
124
# File 'lib/aker/ldap/authority.rb', line 115

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

Instance Method Details

#amplify!(user) ⇒ Aker::User

Merges in the authorization information in this authority for the given user.

Parameters:

Returns:

Since:

  • 2.2.0



335
336
337
338
339
340
341
342
343
# File 'lib/aker/ldap/authority.rb', line 335

def amplify!(user)
  base = self.find_user(user.username)
  return user unless base

  user.extend UserExt
  user.ldap_attributes = base.ldap_attributes

  user.merge!(base)
end

#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



202
203
204
# File 'lib/aker/ldap/authority.rb', line 202

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



238
239
240
# File 'lib/aker/ldap/authority.rb', line 238

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



264
265
266
# File 'lib/aker/ldap/authority.rb', line 264

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



320
321
322
323
324
325
# File 'lib/aker/ldap/authority.rb', line 320

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



271
272
273
274
275
276
277
278
279
280
281
# File 'lib/aker/ldap/authority.rb', line 271

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



156
157
158
# File 'lib/aker/ldap/authority.rb', line 156

def password
  @config[:password]
end

#portInteger

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

Returns:

  • (Integer)

Since:

  • 2.2.0



142
143
144
# File 'lib/aker/ldap/authority.rb', line 142

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

#search_domainObject

The search domain to use when finding user records.

Since:

  • 2.2.0



170
171
172
# File 'lib/aker/ldap/authority.rb', line 170

def search_domain
  @config[:search_domain]
end

#serverString

The configured server’s hostname or other address.

Returns:

  • (String)

Since:

  • 2.2.0



134
135
136
# File 'lib/aker/ldap/authority.rb', line 134

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



164
165
166
# File 'lib/aker/ldap/authority.rb', line 164

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



149
150
151
# File 'lib/aker/ldap/authority.rb', line 149

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



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/aker/ldap/authority.rb', line 292

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