Class: ActiveLdap::Adapter::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/active_ldap/adapter/base.rb,
lib/active_ldap/adapter/ldap.rb,
lib/active_ldap/adapter/net_ldap.rb

Direct Known Subclasses

Ldap, NetLdap

Constant Summary collapse

VALID_ADAPTER_CONFIGURATION_KEYS =
[:host, :port, :method, :timeout,
:retry_on_timeout, :retry_limit,
:retry_wait, :bind_dn, :password,
:password_block, :try_sasl,
:sasl_mechanisms, :sasl_quiet,
:allow_anonymous, :store_password]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(configuration = {}) ⇒ Base

Returns a new instance of Base.



13
14
15
16
17
18
19
20
21
# File 'lib/active_ldap/adapter/base.rb', line 13

def initialize(configuration={})
  @connection = nil
  @configuration = configuration.dup
  @logger = @configuration.delete(:logger)
  @configuration.assert_valid_keys(VALID_ADAPTER_CONFIGURATION_KEYS)
  VALID_ADAPTER_CONFIGURATION_KEYS.each do |name|
    instance_variable_set("@#{name}", configuration[name])
  end
end

Class Method Details

.ldap_connection(options) ⇒ Object



7
8
9
10
11
12
# File 'lib/active_ldap/adapter/ldap.rb', line 7

def ldap_connection(options)
  unless defined?(::LDAP)
    require 'active_ldap/adapter/ldap_ext'
  end
  Ldap.new(options)
end

.net_ldap_connection(options) ⇒ Object



9
10
11
12
13
14
# File 'lib/active_ldap/adapter/net_ldap.rb', line 9

def net_ldap_connection(options)
  unless defined?(::Net::LDAP)
    require 'active_ldap/adapter/net_ldap_ext'
  end
  NetLdap.new(options)
end

Instance Method Details

#add(dn, entries, options = {}) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/active_ldap/adapter/base.rb', line 161

def add(dn, entries, options={})
  begin
    operation(options) do
      yield(dn, entries)
    end
  rescue LdapError::NoSuchObject
    raise EntryNotFound, "No such entry: #{dn}"
  rescue LdapError::InvalidDnSyntax
    raise DistinguishedNameInvalid.new(dn)
  rescue LdapError::AlreadyExists
    raise EntryAlreadyExist, "#{$!.message}: #{dn}"
  rescue LdapError::StrongAuthRequired
    raise StrongAuthenticationRequired, "#{$!.message}: #{dn}"
  rescue LdapError::ObjectClassViolation
    raise RequiredAttributeMissed, "#{$!.message}: #{dn}"
  rescue LdapError::UnwillingToPerform
    raise OperationNotPermitted, "#{$!.message}: #{dn}"
  end
end

#bind(options = {}) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/active_ldap/adapter/base.rb', line 43

def bind(options={})
  bind_dn = options[:bind_dn] || @bind_dn
  try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl
  if options.has_key?(:allow_anonymous)
    allow_anonymous = options[:allow_anonymous]
  else
    allow_anonymous = @allow_anonymous
  end

  # Rough bind loop:
  # Attempt 1: SASL if available
  # Attempt 2: SIMPLE with credentials if password block
  # Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail (or pwblock returns '')
  if try_sasl and sasl_bind(bind_dn, options)
    @logger.info {'Bound SASL'}
  elsif simple_bind(bind_dn, options)
    @logger.info {'Bound simple'}
  elsif allow_anonymous and bind_as_anonymous(options)
    @logger.info {'Bound anonymous'}
  else
    message = yield if block_given?
    message ||= 'All authentication methods exhausted.'
    raise AuthenticationError, message
  end

  bound?
end

#bind_as_anonymous(options = {}) ⇒ Object



71
72
73
74
75
76
# File 'lib/active_ldap/adapter/base.rb', line 71

def bind_as_anonymous(options={})
  @logger.info {"Attempting anonymous authentication"}
  operation(options) do
    yield
  end
end

#connect(options = {}) ⇒ Object



23
24
25
26
27
28
29
30
# File 'lib/active_ldap/adapter/base.rb', line 23

def connect(options={})
  host = options[:host] || @host
  port = options[:port] || @port
  method = ensure_method(options[:method] || @method)
  @connection = yield(host, port, method)
  prepare_connection(options)
  bind(options)
end

#connecting?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/active_ldap/adapter/base.rb', line 78

def connecting?
  not @connection.nil?
end

#delete(targets, options = {}) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/active_ldap/adapter/base.rb', line 146

def delete(targets, options={})
  targets = [targets] unless targets.is_a?(Array)
  return if targets.empty?
  target = nil
  begin
    operation(options) do
      targets.each do |target|
        yield(target)
      end
    end
  rescue LdapError::NoSuchObject
    raise EntryNotFound, "No such entry: #{target}"
  end
end

#disconnect!(options = {}) ⇒ Object



32
33
34
35
36
# File 'lib/active_ldap/adapter/base.rb', line 32

def disconnect!(options={})
  return if @connection.nil?
  unbind(options)
  @connection = nil
end

#load(ldifs, options = {}) ⇒ Object



109
110
111
112
113
114
115
# File 'lib/active_ldap/adapter/base.rb', line 109

def load(ldifs, options={})
  operation(options) do
    ldifs.split(/(?:\r?\n){2,}/).each do |ldif|
      yield(ldif)
    end
  end
end

#modify(dn, entries, options = {}) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
# File 'lib/active_ldap/adapter/base.rb', line 181

def modify(dn, entries, options={})
  begin
    operation(options) do
      yield(dn, entries)
    end
  rescue LdapError::UndefinedType
    raise
  rescue LdapError::ObjectClassViolation
    raise RequiredAttributeMissed, "#{$!.message}: #{dn}"
  end
end

#rebind(options = {}) ⇒ Object



38
39
40
41
# File 'lib/active_ldap/adapter/base.rb', line 38

def rebind(options={})
  unbind(options) if bound?
  connect(options)
end

#schema(options = {}) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/active_ldap/adapter/base.rb', line 82

def schema(options={})
  @schema ||= operation(options) do
    base = options[:base]
    attrs = options[:attributes]

    attrs ||= [
      'objectClasses',
      'attributeTypes',
      'matchingRules',
      'matchingRuleUse',
      'dITStructureRules',
      'dITContentRules',
      'nameForms',
      'ldapSyntaxes',
      #'extendedAttributeInfo', # if we need RANGE-LOWER/UPPER.
    ]
    key = 'subschemaSubentry'
    base ||= root_dse([key], options)[0][key][0]
    base ||= 'cn=schema'
    dn, attributes = search(:base => base,
                            :scope => :base,
                            :filter => '(objectClass=subschema)',
                            :attributes => attrs).first
    Schema.new(attributes)
  end
end

#search(options = {}) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/active_ldap/adapter/base.rb', line 117

def search(options={})
  filter = parse_filter(options[:filter] || 'objectClass=*')
  attrs = options[:attributes] || []
  scope = ensure_scope(options[:scope])
  base = options[:base]
  limit = options[:limit] || 0
  limit = nil if limit <= 0

  attrs = attrs.to_a # just in case

  values = []
  callback = Proc.new do |value, block|
    value = block.call(value) if block
    values << value
  end

  begin
    operation(options) do
      yield(base, scope, filter, attrs, limit, callback)
  end
  rescue LdapError
    # Do nothing on failure
    @logger.debug {"Ignore error #{$!.class}(#{$!.message}) " +
                   "for #{filter} and attrs #{attrs.inspect}"}
  end

  values
end