module LOM::LDAP::Extensions

Provide refinements to ease development with the net/ldap library:

Public Instance Methods

[](name, cnv=nil, &block) click to toggle source
Calls superclass method
# File lib/lom/ldap/extensions.rb, line 239
def [](name, cnv=nil, &block)
    values = super(name)
    if cnv.nil? && block.nil?
    then values
    else values.map {|e| _cast(e, cnv, &block) }
    end
end
Also aliased as: all
_cast(val, cnv=nil, &block) click to toggle source
# File lib/lom/ldap/extensions.rb, line 222
def _cast(val, cnv=nil, &block)
    if cnv && block
        raise ArgumentError,
              'converter can\'t be pass as parameter and as block'
    elsif block
        cnv = block
    end
    
    case cnv
    when Method, Proc then cnv.call(val)
    when Class        then cnv.from_ldap(val)
    when nil          then val
    else raise ArgumentError, "unhandled converter type (#{cnv.class})"
    end
end
all(name, cnv=nil, &block)
Alias for: []
close() click to toggle source
# File lib/lom/ldap/extensions.rb, line 84
def close
end
connect(uri=nil, **opts) click to toggle source
# File lib/lom/ldap/extensions.rb, line 68
def connect(uri=nil, **opts)
    if uri
        uri = URI(uri)
        case uri.scheme
        when 'ldap'  then
        when 'ldaps' then opts[:encryption] = :simple_tls
        else raise ArgumentError, "Unsupported protocol #{proto}";
        end
        opts[:host] = uri.host
        opts[:port] = uri.port
    end
    self.new(opts)
end
escape(str) click to toggle source
# File lib/lom/ldap/extensions.rb, line 196
def escape(str)
    str.gsub(/([\x00-\x1f*()\\])/) { '\\%02x' % $1[0].ord }
end
filter(op, *args) click to toggle source
# File lib/lom/ldap/extensions.rb, line 45
def filter(op, *args)
    op, check = case op
                when :or,  '|'  then [ '|',  1.. ]
                when :and, '&'  then [ '&',  1.. ]
                when :not, '!'  then [ '!',  1   ]
                when :ge,  '>=' then [ '>=', 2   ]
                when :eq,  '='  then [ '=',  2   ]
                when :le,  '<=' then [ '<=', 2   ]
                else raise ArgumentError, 'Unknown operation'
                end
    args = args.compact.map(&:strip).reject(&:empty?).map {|a|
        if    ( a[0] == '(' ) && ( a[-1] == ')' ) then a
        elsif ( a[0] != '(' ) && ( a[-1] != ')' ) then "(#{a})"
        else raise ArgumentError, "Bad LDAP filter: #{a}"
        end
    }
    case args.size
    when 0 then nil
    when 1 then args[0]
    else        "(#{op}#{args.join})"
    end
end
first(name, cnv=nil, &block) click to toggle source
Calls superclass method
# File lib/lom/ldap/extensions.rb, line 248
def first(name, cnv=nil, &block)
    if value = super(name)
        _cast(value, cnv, &block)
    end
end
get(dn:, attributes: nil, attributes_only: false, return_result: true, time: nil, deref: :never, &block) click to toggle source
# File lib/lom/ldap/extensions.rb, line 106
def get(dn:, attributes: nil, attributes_only: false,
        return_result: true, time: nil, deref: :never, &block)
    search(:base            => dn,
           :scope           => :base,
           :attributes      => attributes,
           :attributes_only => attributes_only,
           :return_result   => return_result,
           :time            => time,
           :deref           => deref,
           &block)
        .then {|r| return_result ? r&.first : r }
end
sub?(dn, prefix) click to toggle source
# File lib/lom/ldap/extensions.rb, line 202
def sub?(dn, prefix)
    _dn     = Net::LDAP::DN.new(dn    ).to_a
    _prefix = Net::LDAP::DN.new(prefix).to_a
    return nil if _dn.size <= _prefix.size
    sub     = _dn[0 .. - (_prefix.size + 1)]
    return nil if sub.empty?
    Net::LDAP::DN.new(*sub)
end
update(dn: nil, attributes: {}) click to toggle source

Update an existing dn entry. The necessary operation (add/modify/replace) will be built accordingly.

@note the dn can be specified, either in the dn parameter

or as a key in the attributes parameter

@param dn @param attributes

@return [nil] dn doesn't exist so it can't be updated @return [Boolean] operation success

@raise [ArgumentError] if DN missing or incoherent

# File lib/lom/ldap/extensions.rb, line 134
def update(dn: nil, attributes: {})
    # Normalize keys
    attributes = attributes.to_h.dup
    attributes.transform_keys!   {|k| k.downcase.to_sym  }
    attributes.transform_values! {|v| Array(v)           }
    attributes.transform_values! {|v| v.empty? ? nil : v }

    # Sanitize
    _dn = attributes[:dn]
    if _dn && _dn.size > 1
        raise ArgumentError, 'only one DN can be specified'
    end
    if dn.nil? && _dn.nil?
        raise ArgumentError, 'missing DN'
    elsif dn && _dn && dn != _dn.first
        raise ArgumentError, 'attribute DN doesn\'t match provided DN'
    end

    dn              ||= _dn.first
    attributes[:dn]   = [ dn ]
    
    # Retrieve existing attributes
    # Note: dn is always present in entries
    entries = get(dn: dn, attributes: attributes.keys)
    
    # Entry not found
    return nil if entries.nil?

    # Identify keys
    changing = attributes.compact.keys
    removing = attributes.select {|k, v| v.nil? }.keys
    existing = entries.attribute_names
    add      = changing - existing
    modify   = changing & existing 
    delete   = removing & existing 

    # Remove key from update if same content
    modify.reject! {|k| attributes[k] == entries[k] }
        
    # Build operations
    # Note: order is delete/modify/add
    #       to avoid "Object Class Violation" due to possible
    #       modification of objectClass
    ops = []
    ops += delete.map {|k| [ :delete,  k, nil           ] }
    ops += modify.map {|k| [ :replace, k, attributes[k] ] }
    ops += add   .map {|k| [ :add,     k, attributes[k] ] }

    # Apply
    if LOM.debug.include?(:verbose)
        $stderr.puts "Update: #{dn}"
        $stderr.puts ops.inspect
    end
    if LOM.debug.include?(:dry)
        return true
    end
    return true if ops.empty?               # That's a no-op
    modify(:dn => dn, :operations => ops)   # Apply modifications
end