diff options
author | Luke Kanies <luke@madstop.com> | 2008-05-12 17:00:48 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2008-05-12 17:00:48 -0500 |
commit | 17e8158e35336291c551da03067b55dd815ab539 (patch) | |
tree | 7783b1f3d08ea9eeea7116d522018acabf438f10 /lib/puppet/provider | |
parent | c56e9a6a0a9491270e22363e750046f284ee2793 (diff) | |
download | puppet-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.rb | 37 | ||||
-rw-r--r-- | lib/puppet/provider/ldap.rb | 137 | ||||
-rw-r--r-- | lib/puppet/provider/user/ldap.rb | 115 |
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 |