Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: fdffa16324
Fetching contributors…

Cannot retrieve contributors at this time

385 lines (350 sloc) 12.472 kb
require "dm-core"
require 'slf4r/logger'
# require 'adapters/noop_transaction'
module Ldap
# the class provides two ways of getting a LdapFacade. either
# one which is put on the current Thread or a new one
class LdapConnection
include ::Slf4r::Logger
def initialize(options)
if options[:facade].nil?
require 'ldap/net_ldap_facade'
@facade = ::Ldap::NetLdapFacade
else
case options[:facade].to_sym
when :ruby_ldap
require 'ldap/ruby_ldap_facade'
@facade = ::Ldap::RubyLdapFacade
when :net_ldap
require 'ldap/net_ldap_facade'
@facade = ::Ldap::NetLdapFacade
else
"please add a :facade parameter to the adapter setup. possible values are :ruby_ldap or net_ldap"
end
end
logger.info("using #{@facade}")
@ldaps = { }
@config = {
:host => options[:host],
:port => options[:port].to_i,
:base => options[:base]
}
if not options[:bind_name].nil? then
@config.update({
:auth => {
:method => :simple,
:username => options[:bind_name],
:password => options[:password]
}
})
end
if not options[:adapter_options].nil? then
options[:adapter_options].each do |k,v|
@config[k.to_sym] = v
end
end
end
# puts a LdapFacade into the current thread and executes the
# given block.
def open
begin
@facade.open(@config) do |ldap|
@ldaps[Thread.current] = @facade.new(ldap)
yield
end
ensure
@ldaps[Thread.current] = nil
end
end
# @return [Ldap::LdapFacade]
# either the one from the current Thread or a new one
def current
ldap = @ldaps[Thread.current]
if ldap
ldap
else
@facade.new(@config)
end
end
end
end
require "dm-core"
module DataMapper
class Query
class SortCaseInsensitive < Sort
def initialize(value, ascending = true)
if(value && value.is_a?(String))
super(value.upcase, ascending)
else
super
end
end
end
def sort_records_case_insensitive(records)
#Return unsorted records unless we have order defined
return records unless order
sort_order = order.map { |direction| [ direction.target, direction.operator == :asc ] }
records.sort_by do |record|
sort_order.map do |(property, ascending)|
SortCaseInsensitive.new(record_value(record, property), ascending)
end
end
end
end
module Adapters
class LdapAdapter < AbstractAdapter
# @return [Ldap::LdapFacade]
# ready to use LdapFacade
def ldap
@ldap_connection.current
end
def open_ldap_connection(&block)
@ldap_connection.open(&block)
end
def key_properties(resource)
resource.model.key.first
end
COMPARATORS = { "=" => :eql, ">=" => :gte, "<=" => :lte, "like" => :like }
# helper to remove datamapper specific classes from the conditions
# @param [Array] conditions
# array of tuples: (action, property, new value)
# @return [Array]
# tuples: (action, attribute name, new value)
def to_ldap_conditions(query)
conditions = query.conditions
ldap_conditions = []
conditions.operands.each do |c|
if c.is_a? Array
props = {}
query.fields.each{ |f| props[f.name] = f.field}
or_conditions = []
c[0].split('or').each do |e|
e.strip!
match = e.match("=|<=|>=|like")
or_conditions << [COMPARATORS[match.values_at(0)[0]],
props[match.pre_match.strip.to_sym],
match.post_match.strip.gsub(/'/, '')]
end
ldap_conditions << [:or_operator, or_conditions, nil]
else
comparator = c.slug
case comparator
when :raw
when :not
# TODO proper recursion !!!
ldap_conditions << [comparator, c.operands.first.subject.field, c.operands.first.send(:dumped_value)]
when :in
ldap_conditions << [:eql, c.subject.field, c.send(:dumped_value)]
else
if c.subject.is_a? Ldap::LdapArray
# assume a single value here !!!
val = c.send(:dumped_value)
ldap_conditions << [comparator, c.subject.field, val[1, val.size - 2]]
else
ldap_conditions << [comparator, c.subject.field, c.send(:dumped_value)]
end
end
end
end
ldap_conditions
end
include ::Slf4r::Logger
# @see AbstractAdapter
# def transaction_primitive
# NoopTransaction.new
# end
public
def initialize(name, options)
super(name, options)
@ldap_connection = ::Ldap::LdapConnection.new(@options)
end
def create(resources)
resources.select do |resource|
create_resource(resource)
end.size # just return the number of create resources
end
def update(attributes, collection)
collection.each do |resource|
#puts "update"
#p resource
update_resource(resource, attributes)
end.size
end
# @param [DataMapper::Resource] resource
# to be created
# @see SimpleAdapter#create_resource
# @return [Fixnum]
# value for the primary key or nil
def create_resource(resource)
logger.debug { resource.inspect }
props = resource.model.ldap_properties(resource)
key = nil
resource.send(:properties).each do |prop|
value = prop.get!(resource)
if prop.class == ::Ldap::LdapArray
props[prop.field.to_sym] = value unless value.nil? or value.size == 0
else
props[prop.field.to_sym] = value.to_s unless value.nil?
end
key = prop if prop.serial?
end
resource_dup = resource.dup
if props[key_properties(resource).field.to_sym].nil? then
id = ldap.retrieve_next_id(resource.model.treebase,
key_properties(resource).field)
resource_dup.send("#{key_properties(resource).name}=".to_sym, id)
props[key_properties(resource).field.to_sym] = "#{id}"
end
key_value = begin
ldap.create_object(resource.model.dn_prefix(resource_dup),
resource.model.treebase,
key_properties(resource).field,
props, resource.model.multivalue_field)
rescue => e
raise e unless resource.model.multivalue_field
# TODO something with creating these multivalue objects
end
logger.debug { "resource #{resource.inspect} key value: #{key_value.inspect}" + ", multivalue_field: " + resource.model.multivalue_field.to_s }
if key_value and !key.nil?
key.set!(resource, key_value.to_i)
resource
elsif resource.model.multivalue_field
multivalue_prop = resource.send(:properties).detect do |prop|
prop.field.to_sym == resource.model.multivalue_field
end
update_resource(resource,
{ multivalue_prop =>
resource.send(multivalue_prop.name.to_sym)})
else
nil
end
end
# @param [DataMapper::Resource] resource
# to be updated
# @param [Hash] attributes
# new attributes for the resource
# @see SimpleAdapter#update_resource
def update_resource(resource, attributes)
actions = []
attributes.each do |property, value|
field = property.field.to_sym #TODO sym needed or string ???
if property.class == ::Ldap::LdapArray
value = property.load(value)
if resource.original_attributes[property].nil?
value.each do |v|
actions << [:add, field, v]
end
else
array_actions = []
resource.original_attributes[property].each do |ov|
unless value.member? ov
actions << [:delete, field, ov.to_s]
end
end
value.each do |v|
unless resource.original_attributes[property].member? v
actions << [:add, field, v.to_s]
end
end
array_actions
end
else
if resource.model.multivalue_field == property.field.to_sym
if value.nil?
actions << [:delete, field, resource.attribute_get(property.name).to_s]
else
actions << [:add, field, value.to_s]
end
elsif value.nil?
actions << [:delete, field, []]
elsif resource.original_attributes[property].nil?
actions << [:add, field, value.to_s]
else
actions << [:replace, field, value.to_s]
end
end
end
#puts "actions"
#p actions
#puts
ldap.update_object(resource.model.dn_prefix(resource),
resource.model.treebase,
actions)
end
# @see AbstractAdapter#delete
def delete(collection)
collection.each do |resource|
if resource.model.multivalue_field
multivalue_prop = resource.send(:properties).detect do |prop|
prop.field.to_sym == resource.model.multivalue_field
end
update_resource(resource,
{ multivalue_prop => nil })
else
ldap.delete_object(resource.model.dn_prefix(resource),
resource.model.treebase)
end
end
end
# @see AbstractAdapter#read
def read(query)
result = []
#get data values out of the query
resources = read_resources(query)
resources.each do |resource|
map = {}
query.fields.each_with_index do |property, idx|
map[property.field] = property.typecast(resource[idx])
end
result << map
end
#puts "read_many"
#p result
result = result.uniq if query.unique?
result = query.match_records(result) if query.model.multivalue_field
result = query.sort_records_case_insensitive(result)
result = query.limit_records(result)
result
end
def read_resources(query)
query_order = query.order.first.target.field if query.order
#Use empty string as default for order in query
order_by = query_order || ''
field_names = query.fields.collect {|f| f.field }
result = ldap.read_objects(query.model.treebase,
query.model.key.collect { |k| k.field },
to_ldap_conditions(query),
field_names, order_by)
if query.model.multivalue_field
props_result = []
result.each do |props|
# run over all values of the multivalue field
(props[query.model.multivalue_field] || []).each do |value|
values = query.fields.collect do |f|
if query.model.multivalue_field == f.field.to_sym
value
else
prop = props[f.field.to_sym].first
f.primitive == Integer ? prop.to_i : prop.join rescue prop
end
end
props_result << values
end
end
props_result
else # no multivalue field
result.collect do |props|
query.fields.collect do |f|
prop = props[f.field.to_sym]
if f.class == Ldap::LdapArray
prop if prop
elsif prop
f.primitive == Integer ? prop.first.to_i : prop.join
end
end
end
end
end
end
end
end
Jump to Line
Something went wrong with that request. Please try again.