lib/ruby_sync/connectors/ldap_connector.rb in rubysync-0.0.3 vs lib/ruby_sync/connectors/ldap_connector.rb in rubysync-0.0.4
- old
+ new
@@ -21,12 +21,10 @@
require 'net/ldif'
$VERBOSE = false
require 'net/ldap'
#$VERBOSE = true
-RUBYSYNC_ASSOCIATION_ATTRIBUTE = "RubySyncAssociation"
-RUBYSYNC_ASSOCIATION_CLASS = "RubySyncSynchable"
class Net::LDAP::Entry
def to_hash
return @myhash.dup
end
@@ -34,100 +32,38 @@
module RubySync::Connectors
class LdapConnector < RubySync::Connectors::BaseConnector
option :host,
- :port,
- :bind_method,
- :username,
- :password,
- :search_filter,
- :search_base,
- :association_attribute, # name of the attribute in which to store the association key(s)
- :changelog_dn
+ :port,
+ :bind_method,
+ :username,
+ :password,
+ :search_filter,
+ :search_base,
+ :association_attribute # name of the attribute in which to store the association key(s)
association_attribute 'RubySyncAssociation'
bind_method :simple
host 'localhost'
port 389
search_filter "cn=*"
- changelog_dn "cn=changelog"
def initialize options={}
- super options
- @last_change_number = 1
- # TODO: Persist the current CSN, for now we'll just skip to the end of the changelog
- skip_existing_changelog_entries
+ super options
end
def started
#TODO: If vault, check the schema to make sure that the association_attribute is there
end
-
- # Look for changelog entries. This is not supported by all LDAP servers
- # you may need to subclass for OpenLDAP and Active Directory
- # Changelog entries have these attributes
- # targetdn
- # changenumber
- # objectclass
- # changes
- # changetime
- # changetype
- # dn
- #
- # TODO: Detect presence/location of changelog from root DSE
- def each_change
- with_ldap do |ldap|
- log.debug "@last_change_number = #{@last_change_number}"
- filter = "(changenumber>=#{@last_change_number})"
- first = true
- @full_refresh_required = false
- ldap.search :base => changelog_dn, :filter =>filter do |change|
- change_number = change.changenumber[0].to_i
- if first
- first = false
- # TODO: Persist the change_number so that we don't do a full resync everytime rubysync starts
- if change_number != @last_change_number
- log.warn "Earliest change number (#{change_number}) differs from that recorded (#{@last_change_number})."
- log.warn "A full refresh is required."
- @full_refresh_required = true
- break
- end
- else
- @last_change_number = change_number if change_number > @last_change_number
- # todo: A proper DN object would be nice instead of string manipulation
- target_dn = change.targetdn[0].gsub(/\s*,\s*/,',')
- if target_dn =~ /#{search_base}$/oi
- change_type = change.changetype[0]
- event = event_for_changelog_entry(change)
- yield event
- end
- end
- end
- end
- end
-
-
- def skip_existing_changelog_entries
- with_ldap do |ldap|
- filter = "(changenumber>=#{@last_change_number})"
- @full_refresh_required = false
- ldap.search :base => changelog_dn, :filter =>filter do |change|
- change_number = change.changenumber[0].to_i
- @last_change_number = change_number if change_number > @last_change_number
- end
- end
- end
def each_entry
Net::LDAP.open(:host=>host, :port=>port, :auth=>auth) do |ldap|
- ldap.search :base => search_base, :filter => search_filter do |entry|
- operations = operations_for_entry(entry)
- association_key = (is_vault?)? nil : entry.dn
- yield RubySync::Event.add(self, entry.dn, association_key, operations)
+ ldap.search :base => search_base, :filter => search_filter do |ldap_entry|
+ yield ldap_entry.dn, to_entry(ldap_entry)
end
end
end
# Runs the query specified by the config, gets the objectclass of the first
@@ -135,39 +71,39 @@
def self.fields
log.warn "Fields method not yet implemented for LDAP - Sorry."
log.warn "Returning a likely sample set."
%w{ cn givenName sn }
end
-
- def stopped
- end
-
def self.sample_config
return <<END
host 'localhost'
- port 10389
- username 'uid=admin,ou=system'
+ port 389
+ username 'cn=Manager,dc=my-domain,dc=com'
password 'secret'
search_filter "cn=*"
- search_base "dc=example,dc=com"
+ search_base "ou=users,o=my-organization,dc=my-domain,dc=com"
#:bind_method :simple
END
end
def add(path, operations)
+ result = nil
with_ldap do |ldap|
- ldap.add :dn=>path, :attributes=>perform_operations(operations)
+ attributes = perform_operations(operations)
+ result = ldap.add :dn=>path, :attributes=>attributes
end
+ log.debug("ldap.add returned '#{result}'")
return true
- rescue Net::LdapException
- log.warning "Exception occurred while adding LDAP record"
+ rescue Exception
+ log.warn "Exception occurred while adding LDAP record"
+ log.debug $!
false
end
def modify(path, operations)
log.debug "Modifying #{path} with the following operations:\n#{operations.inspect}"
@@ -190,139 +126,40 @@
end
end
# Called by unit tests to inject data
def test_add id, details
- details << RubySync::Operation.new(:add, "objectclass", ['inetOrgPerson', 'organizationalPerson', 'person', 'top', 'RubySyncSynchable'])
+ details << RubySync::Operation.new(:add, "objectclass", ['inetOrgPerson'])
add id, details
end
def target_transform event
#event.add_default 'objectclass', 'inetOrgUser'
#is_vault? and event.add_value 'objectclass', RUBYSYNC_ASSOCIATION_CLASS
end
- def associate association, path
- with_ldap do |ldap|
- # todo: check and warn if path is outside of search_base
- ldap.modify :dn=>path, :operations=>[
- [:add, RUBYSYNC_ASSOCIATION_ATTRIBUTE, association.to_s]
- ]
- end
- end
-
- def path_for_association association
- with_ldap do |ldap|
- filter = "#{RUBYSYNC_ASSOCIATION_ATTRIBUTE}=#{association.to_s}"
- log.debug "Searching with filter: #{filter}"
- results = ldap.search :base=>@search_base,
- :filter=>filter,
- :attributes=>[]
- results or return nil
- case results.length
- when 0: return nil
- when 1: return results[0].dn
- else
- raise Exception.new("Duplicate association found for #{association.to_s}")
- end
- end
- end
-
- def associations_for path
- with_ldap do |ldap|
- results = ldap.search :base=>path,
- :scope=>Net::LDAP::SearchScope_BaseObject,
- :attributes=>[RUBYSYNC_ASSOCIATION_ATTRIBUTE]
- unless results and results.length > 0
- log.warn "Attempted association lookup on non-existent LDAP entry '#{path}'"
- return []
- end
- associations = results[0][RUBYSYNC_ASSOCIATION_ATTRIBUTE]
- return (associations)? associations.as_array : []
- end
- end
-
- def remove_association association
- path = path_for_association association
- with_ldap do |ldap|
- ldap.modify :dn=>path, :modifications=>[
- [:delete, RUBYSYNC_ASSOCIATION_ATTRIBUTE, association.to_s]
- ]
- end
- end
- # def associate_with_foreign_key key, path
- # with_ldap do |ldap|
- # ldap.add_attribute(path, association_attribute, key.to_s)
- # end
- # end
- #
- # def path_for_foreign_key key
- # entry = entry_for_foreign_key key
- # (entry)? entry.dn : nil
- # end
- #
- # def foreign_key_for path
- # entry = self[path]
- # (entry)? entry.dn : nil # TODO: That doesn't look right. Should return an association key, not a path.
- # end
- #
- # def remove_foreign_key key
- # with_ldap do |ldap|
- # entry = entry_for_foreign_key key
- # if entry
- # modify :dn=>entry.dn, :operations=>[ [:delete, association_attribute, key] ]
- # end
- # end
- # end
- #
- # def find_associated foreign_key
- # entry = entry_for_foreign_key key
- # (entry)? operations_for_entry(entry) : nil
- # end
+ private
-
-private
-
- def event_for_changelog_entry cle
- payload = nil
- dn = cle.targetdn[0]
- changetype = cle.changetype[0]
- if cle.attribute_names.include? :changes
- payload = []
- cr = Net::LDIF.parse("dn: #{dn}\nchangetype: #{changetype}\n#{cle.changes[0]}")[0]
- if changetype.to_sym == :add
- # cr.data will be a hash of arrays or strings (attr-name=>[value1, value2, ...])
- cr.data.each do |name, values|
- payload << RubySync::Operation.add(name, values)
- end
- else
- # cr.data will be an array of arrays of form [:action, :subject, [values]]
- cr.data.each do |record|
- payload << RubySync::Operation.new(record[0], record[1], record[2])
- end
- end
+ def to_entry ldap_entry
+ entry = {}
+ ldap_entry.each do |name, values|
+ entry[name.to_s] = values.map {|v| String.new(v)}
end
- RubySync::Event.new(changetype, self, dn, nil, payload)
+ entry
end
-
def operations_for_entry entry
ops = []
entry.each do |name, values|
ops << RubySync::Operation.add(name, values)
end
ops
end
- def entry_for_foreign_key key
- with_ldap do |ldap|
- result = ldap.search :base=>search_base, :filter=>"#{association_attribute}=#{key}"
- return nil if !result or result.size == 0
- result[0]
- end
- end
+
+
def with_ldap
result = nil
Net::LDAP.open(:host=>host, :port=>port, :auth=>auth) do |ldap|