summaryrefslogtreecommitdiffstats
path: root/lib/puppet/provider
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-05-12 17:00:48 -0500
committerLuke Kanies <luke@madstop.com>2008-05-12 17:00:48 -0500
commit17e8158e35336291c551da03067b55dd815ab539 (patch)
tree7783b1f3d08ea9eeea7116d522018acabf438f10 /lib/puppet/provider
parentc56e9a6a0a9491270e22363e750046f284ee2793 (diff)
downloadpuppet-17e8158e35336291c551da03067b55dd815ab539.tar.gz
puppet-17e8158e35336291c551da03067b55dd815ab539.tar.xz
puppet-17e8158e35336291c551da03067b55dd815ab539.zip
Adding ldap providers for the user and group type.
These providers use posixAccount and posixGroup. This is a collapsed merge, fwiw.
Diffstat (limited to 'lib/puppet/provider')
-rw-r--r--lib/puppet/provider/group/ldap.rb37
-rw-r--r--lib/puppet/provider/ldap.rb137
-rw-r--r--lib/puppet/provider/user/ldap.rb115
3 files changed, 289 insertions, 0 deletions
diff --git a/lib/puppet/provider/group/ldap.rb b/lib/puppet/provider/group/ldap.rb
new file mode 100644
index 000000000..632358ff1
--- /dev/null
+++ b/lib/puppet/provider/group/ldap.rb
@@ -0,0 +1,37 @@
+require 'puppet/provider/ldap'
+
+Puppet::Type.type(:group).provide :ldap, :parent => Puppet::Provider::Ldap do
+ desc "Group management via ``ldap``. This provider requires that you
+ have valid values for all of the ldap-related settings,
+ including ``ldapbase``. You will also almost definitely need settings
+ for ``ldapuser`` and ``ldappassword``, so that your clients can write
+ to ldap.
+
+ Note that this provider will automatically generate a GID for you if
+ you do not specify one, but it is a potentially expensive operation,
+ as it iterates across all existing groups to pick the appropriate next
+ one."
+
+ confine :true => Puppet.features.ldap?
+
+ # We're mapping 'members' here because we want to make it
+ # easy for the ldap user provider to manage groups. This
+ # way it can just use the 'update' method in the group manager,
+ # whereas otherwise it would need to replicate that code.
+ manages(:posixGroup).at("ou=Groups").and.maps :name => :cn, :gid => :gidNumber, :members => :memberUid
+
+ # Find the next gid after the current largest gid.
+ provider = self
+ manager.generates(:gidNumber).with do
+ largest = 0
+ provider.manager.search.each do |hash|
+ next unless value = hash[:gid]
+ num = value[0].to_i
+ if num > largest
+ largest = num
+ end
+ end
+ largest + 1
+ end
+
+end
diff --git a/lib/puppet/provider/ldap.rb b/lib/puppet/provider/ldap.rb
new file mode 100644
index 000000000..76834f94d
--- /dev/null
+++ b/lib/puppet/provider/ldap.rb
@@ -0,0 +1,137 @@
+require 'puppet/provider'
+
+# The base class for LDAP providers.
+class Puppet::Provider::Ldap < Puppet::Provider
+ require 'puppet/util/ldap/manager'
+
+ class << self
+ attr_reader :manager
+ end
+
+ # Look up all instances at our location. Yay.
+ def self.instances
+ return [] unless list = manager.search
+
+ list.collect { |entry| new(entry) }
+ end
+
+ # Specify the ldap manager for this provider, which is
+ # used to figure out how we actually interact with ldap.
+ def self.manages(*args)
+ @manager = Puppet::Util::Ldap::Manager.new
+ @manager.manages(*args)
+
+ # Set up our getter/setter methods.
+ mk_resource_methods
+ return @manager
+ end
+
+ # Query all of our resources from ldap.
+ def self.prefetch(resources)
+ resources.each do |name, resource|
+ if result = manager.find(name)
+ result[:ensure] = :present
+ resource.provider = new(result)
+ else
+ resource.provider = new(:ensure => :absent)
+ end
+ end
+ end
+
+ attr_reader :ldap_properties
+
+ def manager
+ self.class.manager
+ end
+
+ def create
+ @property_hash[:ensure] = :present
+ self.class.resource_type.validproperties.each do |property|
+ if val = resource.should(property)
+ @property_hash[property] = val
+ end
+ end
+ end
+
+ def delete
+ @property_hash[:ensure] = :absent
+ end
+
+ def exists?
+ @property_hash[:ensure] != :absent
+ end
+
+ # Apply our changes to ldap, yo.
+ def flush
+ # Just call the manager's update() method.
+ @property_hash.delete(:groups)
+ @ldap_properties.delete(:groups)
+ manager.update(name, ldap_properties, properties)
+ @property_hash.clear
+ @ldap_properties.clear
+ end
+
+ def initialize(*args)
+ raise(Puppet::DevError, "No LDAP Configuration defined for %s" % self.class) unless self.class.manager
+ raise(Puppet::DevError, "Invalid LDAP Configuration defined for %s" % self.class) unless self.class.manager.valid?
+ super
+
+ @property_hash = @property_hash.inject({}) do |result, ary|
+ param, values = ary
+
+ # Skip any attributes we don't manage.
+ next result unless self.class.resource_type.validattr?(param)
+
+ paramclass = self.class.resource_type.attrclass(param)
+
+ unless values.is_a?(Array)
+ result[param] = values
+ next result
+ end
+
+ # Only use the first value if the attribute class doesn't manage
+ # arrays of values.
+ if paramclass.superclass == Puppet::Parameter or paramclass.array_matching == :first
+ result[param] = values[0]
+ else
+ result[param] = values
+ end
+ result
+ end
+
+ # Make a duplicate, so that we have a copy for comparison
+ # at the end.
+ @ldap_properties = @property_hash.dup
+ end
+
+ # Return the current state of ldap.
+ def ldap_properties
+ @ldap_properties.dup
+ end
+
+ # Return (and look up if necessary) the desired state.
+ def properties
+ if @property_hash.empty?
+ @property_hash = query || {:ensure => :absent}
+ if @property_hash.empty?
+ @property_hash[:ensure] = :absent
+ end
+ end
+ @property_hash.dup
+ end
+
+ # Collect the current attributes from ldap. Returns
+ # the results, but also stores the attributes locally,
+ # so we have something to compare against when we update.
+ # LAK:NOTE This is normally not used, because we rely on prefetching.
+ def query
+ # Use the module function.
+ unless attributes = manager.find(name)
+ @ldap_properties = {}
+ return nil
+ end
+
+ @ldap_properties = attributes
+ return @ldap_properties.dup
+ end
+end
diff --git a/lib/puppet/provider/user/ldap.rb b/lib/puppet/provider/user/ldap.rb
new file mode 100644
index 000000000..ba91a871e
--- /dev/null
+++ b/lib/puppet/provider/user/ldap.rb
@@ -0,0 +1,115 @@
+require 'puppet/provider/ldap'
+
+Puppet::Type.type(:user).provide :ldap, :parent => Puppet::Provider::Ldap do
+ desc "User management via ``ldap``. This provider requires that you
+ have valid values for all of the ldap-related settings,
+ including ``ldapbase``. You will also almost definitely need settings
+ for ``ldapuser`` and ``ldappassword``, so that your clients can write
+ to ldap.
+
+ Note that this provider will automatically generate a UID for you if
+ you do not specify one, but it is a potentially expensive operation,
+ as it iterates across all existing users to pick the appropriate next
+ one."
+
+ confine :true => Puppet.features.ldap?
+
+ manages(:posixAccount, :person).at("ou=People").named_by(:uid).and.maps :name => :uid,
+ :password => :userPassword,
+ :comment => :cn,
+ :uid => :uidNumber,
+ :gid => :gidNumber,
+ :home => :homeDirectory,
+ :shell => :loginShell
+
+ # Use the last field of a space-separated array as
+ # the sn. LDAP requires a surname, for some stupid reason.
+ manager.generates(:sn).from(:cn).with do |cn|
+ x = 1
+ cn[0].split(/\s+/)[-1]
+ end
+
+ # Find the next uid after the current largest uid.
+ provider = self
+ manager.generates(:uidNumber).with do
+ largest = 0
+ provider.manager.search.each do |hash|
+ next unless value = hash[:uid]
+ num = value[0].to_i
+ if num > largest
+ largest = num
+ end
+ end
+ largest + 1
+ end
+
+ # Find all groups this user is a member of in ldap.
+ def groups
+ # We want to cache the current result, so we know if we
+ # have to remove old values.
+ unless @property_hash[:groups]
+ unless result = group_manager.search("memberUid=%s" % name)
+ return @property_hash[:groups] = :absent
+ end
+
+ return @property_hash[:groups] = result.collect { |r| r[:name] }.join(",")
+ end
+ return @property_hash[:groups]
+ end
+
+ # Manage the list of groups this user is a member of.
+ def groups=(values)
+ should = values.split(",")
+
+ if groups() == :absent
+ is = []
+ else
+ is = groups().split(",")
+ end
+
+ modes = {}
+ [is, should].flatten.uniq.each do |group|
+ # Skip it when they're in both
+ next if is.include?(group) and should.include?(group)
+
+ # We're adding a group.
+ modes[group] = :add and next unless is.include?(group)
+
+ # We're removing a group.
+ modes[group] = :remove and next unless should.include?(group)
+ end
+
+ modes.each do |group, form|
+ self.fail "Could not find ldap group %s" % group unless ldap_group = group_manager.find(group)
+
+ current = ldap_group[:members]
+
+ if form == :add
+ if current.is_a?(Array) and ! current.empty?
+ new = current + [name]
+ else
+ new = [name]
+ end
+ else
+ new = current - [name]
+ new = :absent if new.empty?
+ end
+
+ group_manager.update(group, {:ensure => :present, :members => current}, {:ensure => :present, :members => new})
+ end
+ end
+
+ private
+
+ def group_manager
+ Puppet::Type.type(:group).provider(:ldap).manager
+ end
+
+ def group_properties(values)
+ if values.empty? or values == :absent
+ {:ensure => :present}
+ else
+ {:ensure => :present, :members => values}
+ end
+ end
+end