1
require 'puppet/util/ldap'
2
require 'puppet/util/ldap/connection'
3
require 'puppet/util/ldap/generator'
5
# The configuration class for LDAP providers, plus
6
# connection handling for actually interacting with ldap.
7
class Puppet::Util::Ldap::Manager
8
attr_reader :objectclasses, :puppet2ldap, :location, :rdn
10
# A null-op that just returns the config.
15
# Set the offset from the search base and return the config.
21
# The basic search base.
23
[location, Puppet[:ldapbase]].join(",")
26
# Convert the name to a dn, then pass the args along to
28
def create(name, attributes)
29
attributes = attributes.dup
31
# Add the objectclasses
32
attributes["objectClass"] = objectclasses.collect { |o| o.to_s }
33
attributes["objectClass"] << "top" unless attributes["objectClass"].include?("top")
35
attributes[rdn.to_s] = [name]
37
# Generate any new values we might need.
40
# And create our resource.
41
connect { |conn| conn.add dn(name), attributes }
44
# Open, yield, and close the connection. Cannot be left
45
# open, at this point.
47
raise ArgumentError, "You must pass a block to #connect" unless block_given?
49
unless defined?(@connection) and @connection
52
elsif Puppet[:ldapssl]
57
options = {:ssl => ssl}
58
if user = Puppet[:ldapuser] and user != ""
61
if password = Puppet[:ldappassword] and password != ""
62
options[:password] = password
64
@connection = Puppet::Util::Ldap::Connection.new(Puppet[:ldapserver], Puppet[:ldapport], options)
68
yield @connection.connection
75
# Convert the name to a dn, then pass the args along to
78
connect { |connection| connection.delete dn(name) }
81
# Calculate the dn for a given resource.
83
["#{rdn.to_s}=%s" % name, base].join(",")
86
# Convert an ldap-style entry hash to a provider-style hash.
87
def entry2provider(entry)
88
raise ArgumentError, "Could not get dn from ldap entry" unless entry["dn"]
90
# DN is always a single-entry array. Strip off the bits before the
91
# first comma, then the bits after the remaining equal sign. This is the
93
name = entry["dn"].dup.pop.split(",").shift.split("=").pop
95
result = {:name => name}
97
@ldap2puppet.each do |ldap, puppet|
98
result[puppet] = entry[ldap.to_s] || :absent
104
# Create our normal search filter.
106
return "objectclass=%s" % objectclasses[0] if objectclasses.length == 1
107
return "(&(objectclass=" + objectclasses.join(")(objectclass=") + "))"
110
# Find the associated entry for a resource. Returns a hash, minus
111
# 'dn', or nil if the entry cannot be found.
116
conn.search2(dn(name), 0, "objectclass=*") do |result|
117
# Convert to puppet-appropriate attributes
118
return entry2provider(result)
126
# Declare a new attribute generator.
127
def generates(parameter)
128
@generators << Puppet::Util::Ldap::Generator.new(parameter)
132
# Generate any extra values we need to make the ldap entry work.
134
return unless @generators.length > 0
136
@generators.each do |generator|
137
# Don't override any values that might exist.
138
next if values[generator.name]
141
unless value = values[generator.source]
142
raise ArgumentError, "%s must be defined to generate %s" % [generator.source, generator.name]
144
result = generator.generate(value)
146
result = generator.generate
149
result = [result] unless result.is_a?(Array)
150
result = result.collect { |r| r.to_s }
152
values[generator.name] = result
161
# Specify what classes this provider models.
162
def manages(*classes)
163
@objectclasses = classes
167
# Specify the attribute map. Assumes the keys are the puppet
168
# attributes, and the values are the ldap attributes, and creates a map
169
# for each direction.
171
# The map with the puppet attributes as the keys
172
@puppet2ldap = attributes
174
# and the ldap attributes as the keys.
175
@ldap2puppet = attributes.inject({}) { |map, ary| map[ary[1]] = ary[0]; map }
180
# Return the ldap name for a puppet attribute.
181
def ldap_name(attribute)
182
@puppet2ldap[attribute].to_s
185
# Convert the name to a dn, then pass the args along to
187
def modify(name, mods)
188
connect { |connection| connection.modify dn(name), mods }
191
# Specify the rdn that we use to build up our dn.
192
def named_by(attribute)
197
# Return the puppet name for an ldap attribute.
198
def puppet_name(attribute)
199
@ldap2puppet[attribute]
202
# Search for all entries at our base. A potentially expensive search.
203
def search(sfilter = nil)
208
conn.search2(base, 1, sfilter) do |entry|
209
result << entry2provider(entry)
212
return nil if result.empty?
216
# Update the ldap entry with the desired state.
217
def update(name, is, should)
218
if should[:ensure] == :absent
219
Puppet.info "Removing %s from ldap" % dn(name)
224
# We're creating a new entry
225
if is.empty? or is[:ensure] == :absent
226
Puppet.info "Creating %s in ldap" % dn(name)
227
# Remove any :absent params and :ensure, then convert the names to ldap names.
228
attrs = ldap_convert(should)
233
# We're modifying an existing entry. Yuck.
236
# For each attribute we're deleting that is present, create a
237
# modify instance for deletion.
238
[is.keys, should.keys].flatten.uniq.each do |property|
239
# They're equal, so do nothing.
240
next if is[property] == should[property]
242
attributes = ldap_convert(should)
244
prop_name = ldap_name(property).to_s
247
if is[property] == :absent or is[property].nil?
248
mods << LDAP::Mod.new(LDAP::LDAP_MOD_ADD, prop_name, attributes[prop_name])
253
if should[property] == :absent or should[property].nil?
254
mods << LDAP::Mod.new(LDAP::LDAP_MOD_DELETE, prop_name, [])
258
# We're replacing an existing value
259
mods << LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE, prop_name, attributes[prop_name])
265
# Is this a complete ldap configuration?
267
location and objectclasses and ! objectclasses.empty? and puppet2ldap
272
# Convert a hash of attributes to ldap-like forms. This mostly means
273
# getting rid of :ensure and making sure everything's an array of strings.
274
def ldap_convert(attributes)
275
attributes.reject { |param, value| value == :absent or param == :ensure }.inject({}) do |result, ary|
276
value = (ary[1].is_a?(Array) ? ary[1] : [ary[1]]).collect { |v| v.to_s }
277
result[ldap_name(ary[0])] = value