Class: Ldaptic::Entry

Inherits:
Object
  • Object
show all
Defined in:
lib/ldaptic/entry.rb

Overview

When a new Ldaptic namespace is created, a Ruby class hierarchy is contructed that mirrors the server’s object classes. Ldaptic::Entry serves as the base class for this hierarchy.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = {}) ⇒ Entry

Returns a new instance of Entry.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/ldaptic/entry.rb', line 171

def initialize(data = {})
  Ldaptic::Errors.raise(TypeError.new("abstract class initialized")) if self.class.oid.nil? || self.class.abstract?
  @attributes = {}
  data = data.dup
  if dn = data.delete('dn') || data.delete(:dn)
    dn = dn.first if dn.kind_of?(Array)
    self.dn = dn
  end
  merge_attributes(data)
  @attributes['objectClass'] ||= []
  @attributes['objectClass'].insert(0, *self.class.object_classes).uniq!
  common_initializations
  after_build
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Delegates to read_attribute or write_attribute. Pops an element out of its set if the attribute is marked SINGLE-VALUE.



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/ldaptic/entry.rb', line 413

def method_missing(method, *args, &block)
  attribute = Ldaptic.encode(method)
  if attribute[-1] == ?=
    attribute.chop!
    if may_must(attribute)
      return write_attribute(attribute, *args, &block)
    end
  elsif attribute[-1] == ??
    attribute.chop!
    if may_must(attribute)
      if args.empty?
        return !read_attribute(attribute).empty?
      else
        return args.flatten.any? {|arg| compare(attribute, arg)}
      end
    end
  elsif attribute =~ /\A(.*)-before-type-cast\z/ && may_must($1)
    return read_attribute($1, *args, &block)
  elsif may_must(attribute)
    return read_attribute(attribute, *args, &block).one
  end
  super(method, *args, &block)
end

Class Attribute Details

.descObject (readonly)

Returns the value of attribute desc.



17
18
19
# File 'lib/ldaptic/entry.rb', line 17

def desc
  @desc
end

.namespaceObject (readonly)

Returns the value of attribute namespace.



64
65
66
# File 'lib/ldaptic/entry.rb', line 64

def namespace
  @namespace
end

.oidObject (readonly)

Returns the value of attribute oid.



17
18
19
# File 'lib/ldaptic/entry.rb', line 17

def oid
  @oid
end

.supObject (readonly)

Returns the value of attribute sup.



17
18
19
# File 'lib/ldaptic/entry.rb', line 17

def sup
  @sup
end

Instance Attribute Details

#dnObject

Returns the value of attribute dn.



215
216
217
# File 'lib/ldaptic/entry.rb', line 215

def dn
  @dn
end

Class Method Details

.attributes(all = true) ⇒ Object



109
110
111
# File 'lib/ldaptic/entry.rb', line 109

def attributes(all = true)
  may(all) + must(all)
end

.auxObject



101
102
103
104
105
106
107
# File 'lib/ldaptic/entry.rb', line 101

def aux
  if dit_content_rule
    Array(dit_content_rule.aux)
  else
    []
  end
end

.create_accessorsObject

:nodoc:



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/ldaptic/entry.rb', line 38

def create_accessors #:nodoc:
  to_be_evaled = ""
  (may(false) + must(false)).each do |attr|
    if type = namespace.attribute_type(attr)
      names = type.names
    else
      names = [attr]
    end
    names.each do |name|
      method = name.to_s.tr_s('-_', '_-')
      to_be_evaled << "def \#{method}() read_attribute('\#{attr}').one end\ndef \#{method}=(value) write_attribute('\#{attr}', value) end\n"
    end
  end
  class_eval(to_be_evaled, __FILE__, __LINE__)
end

.dit_content_ruleObject



113
114
115
# File 'lib/ldaptic/entry.rb', line 113

def dit_content_rule
  namespace.dit_content_rule(oid)
end

.has_attribute?(attribute) ⇒ Boolean

Returns:

  • (Boolean)


33
34
35
36
# File 'lib/ldaptic/entry.rb', line 33

def has_attribute?(attribute)
  attribute = Ldaptic.encode(attribute)
  may.include?(attribute) || must.include?(attribute)
end

.human_attribute_name(attribute, options = {}) ⇒ Object

Converts an attribute name to a human readable form. For compatibility with ActiveRecord.

L::User.human_attribute_name(:givenName) #=> "Given name"


131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/ldaptic/entry.rb', line 131

def human_attribute_name(attribute, options={})
  attribute = Ldaptic.encode(attribute)
  if at = namespace.attribute_type(attribute)
    attribute = at.verbose_name
  end
  attribute = attribute[0..0].upcase + attribute[1..-1]
  attribute.gsub!(/([A-Z])([A-Z][a-z])/) { "#$1 #{$2.downcase}" }
  attribute.gsub!(/([a-z\d])([A-Z])/) { "#$1 #{$2.downcase}" }
  attribute.gsub!(/\b[a-z][A-Z]/) { $&.upcase }
  attribute.gsub!('_', '-')
  attribute
end

.instantiate(attributes) ⇒ Object

:nodoc:



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/ldaptic/entry.rb', line 144

def instantiate(attributes) #:nodoc:
  ocs = attributes["objectClass"].to_a.map {|c| namespace.object_class(c)}
  subclass = (@subclasses.to_a & ocs).detect {|x| !x.auxiliary?}
  if subclass
    return subclass.instantiate(attributes)
  end
  unless structural? || ocs.empty?
    logger.warn "#{name}: invalid object class for #{attributes.inspect}"
  end
  obj = allocate
  obj.instance_variable_set(:@dn, ::Ldaptic::DN([attributes.delete('dn')].flatten.first, obj))
  obj.instance_variable_set(:@original_attributes, attributes)
  obj.instance_variable_set(:@attributes, {})
  obj.instance_eval { common_initializations; after_load }
  obj
end

.ldap_ancestorsObject

An array of classes that make up the inheritance hierarchy.

L::OrganizationalPerson.ldap_ancestors #=> [L::OrganizationalPerson, L::Person, L::Top]


60
61
62
# File 'lib/ldaptic/entry.rb', line 60

def ldap_ancestors
  ancestors.select {|o| o.respond_to?(:oid) && o.oid }
end

.loggerObject



22
23
24
# File 'lib/ldaptic/entry.rb', line 22

def logger
  namespace.logger
end

.may(all = true) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/ldaptic/entry.rb', line 66

def may(all = true)
  if all
    core = []
    nott = []
    ldap_ancestors.reverse.each do |klass|
      core |= Array(klass.may(false))
      nott |= Array(klass.must(false))
    end
    if dit = dit_content_rule
      core.push(*Array(dit.may))
      core -= Array(dit.must)
      core -= Array(dit.not)
    end
    core -= nott
    core
  else
    Array(@may)
  end
end

.model_nameObject

For Active Model compliance. Delegates to #namespace.



12
13
14
# File 'lib/ldaptic/entry.rb', line 12

def self.model_name
  namespace.model_name
end

.must(all = true) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ldaptic/entry.rb', line 86

def must(all = true)
  if all
    core = ldap_ancestors.inject([]) do |memo, klass|
      memo |= Array(klass.must(false))
      memo
    end
    if dit = dit_content_rule
      core.push(*Array(dit.must))
    end
    core
  else
    Array(@must)
  end
end

.namesObject

Returns an array of all names for the object class. Typically the number of names is one, but it is possible for an object class to have aliases.



29
30
31
# File 'lib/ldaptic/entry.rb', line 29

def names
  Array(@name)
end

.object_classObject



117
118
119
# File 'lib/ldaptic/entry.rb', line 117

def object_class
  @object_class || names.first
end

.object_classesObject Also known as: objectClass



121
122
123
# File 'lib/ldaptic/entry.rb', line 121

def object_classes
  ldap_ancestors.map {|a| a.object_class}.compact.reverse.uniq
end

Instance Method Details

#/(*args) ⇒ Object Also known as: find

Searches for a child, given an RDN.



447
448
449
# File 'lib/ldaptic/entry.rb', line 447

def /(*args)
  search(:base => dn.send(:/, *args), :scope => :base, :limit => true)
end

#[](key) ⇒ Object

If a Hash or a String containing “=” is given, the argument is treated as an RDN and a search for a child is performed. nil is returned if no match is found.

For a singular String or Symbol argument, that attribute is read with read_attribute. Unlike with method_missing, an array is always returned, making this variant useful for metaprogramming.



467
468
469
470
471
472
473
# File 'lib/ldaptic/entry.rb', line 467

def [](key)
  if key.kind_of?(Hash) || key =~ /=/
    cached_child(key)
  else
    read_attribute(key)
  end
end

#[]=(key, value) ⇒ Object



475
476
477
478
479
480
481
# File 'lib/ldaptic/entry.rb', line 475

def []=(key, value)
  if key.kind_of?(Hash) || key =~ /=/
    assign_child(key, value)
  else
    write_attribute(key, value)
  end
end

#add!(key, *values) ⇒ Object

:nodoc:



343
344
345
# File 'lib/ldaptic/entry.rb', line 343

def add!(key, *values) #:nodoc:
  modify_attribute(:add, key, values)
end

#attribute_namesObject



361
362
363
# File 'lib/ldaptic/entry.rb', line 361

def attribute_names
  attributes.keys
end

#attributesObject

Returns a hash of attributes.



284
285
286
287
288
289
# File 'lib/ldaptic/entry.rb', line 284

def attributes
  (@original_attributes||{}).merge(@attributes).keys.inject({}) do |hash, key|
    hash[key] = read_attribute(key)
    hash
  end
end

#auxObject



369
370
371
# File 'lib/ldaptic/entry.rb', line 369

def aux
  self['objectClass'].map {|c| namespace.object_class(c)} - self.class.ldap_ancestors
end

#changesObject



291
292
293
294
295
296
297
298
# File 'lib/ldaptic/entry.rb', line 291

def changes
  @attributes.reject do |k, v|
    (@original_attributes || {})[k].to_a == v
  end.keys.inject({}) do |hash, key|
    hash[key] = read_attribute(key)
    hash
  end
end

#compare(key, value) ⇒ Object

Compare an attribute to see if it has a given value. This happens at the server.



357
358
359
# File 'lib/ldaptic/entry.rb', line 357

def compare(key, value)
  namespace.compare(dn, key, value)
end

#deleteObject

Deletes the object from the server. If #save is invoked afterwards, the entry will be recreated.



561
562
563
564
565
566
# File 'lib/ldaptic/entry.rb', line 561

def delete
  namespace.delete(dn)
  @attributes = (@original_attributes||{}).merge(@attributes)
  @original_attributes = nil
  self
end

#delete!(key, *values) ⇒ Object

:nodoc:



351
352
353
# File 'lib/ldaptic/entry.rb', line 351

def delete!(key, *values) #:nodoc:
  modify_attribute(:delete, key, values)
end

#destroyObject

Alias for #delete.



569
570
571
# File 'lib/ldaptic/entry.rb', line 569

def destroy
  delete
end

#errorsObject



488
489
490
# File 'lib/ldaptic/entry.rb', line 488

def errors
  @errors ||= Ldaptic::ErrorSet.new(self)
end

#fetch(dn = self.dn, options = {}) ⇒ Object

:nodoc:



453
454
455
456
457
458
# File 'lib/ldaptic/entry.rb', line 453

def fetch(dn = self.dn, options = {}) #:nodoc:
  if dn.kind_of?(Hash)
    dn = self.dn/dn
  end
  namespace.fetch(dn, options)
end

#inspectObject



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/ldaptic/entry.rb', line 241

def inspect
  str = "#<#{self.class.inspect} #{dn}"
  (@original_attributes||{}).merge(@attributes).each do |k, values|
    next if values.empty?
    s = (values.size == 1 ? "" : "s")
    at = namespace.attribute_type(k)
    syntax = namespace.attribute_syntax(k)
    if at && syntax && !syntax.x_not_human_readable? && syntax.desc != "Octet String"
      str << " " << k << ": " << values.inspect[1..-2]
    else
      str << " " << k << ": "
      if !at
        str << "(unknown attribute)"
      elsif !syntax
        str << "(unknown type)"
      else
        str << "(" << values.size.to_s << " binary value" << s << ")"
      end
    end
  end
  str << ">"
end

#invalid?(*args) ⇒ Boolean

Inverse of #valid?

Returns:

  • (Boolean)


499
500
501
# File 'lib/ldaptic/entry.rb', line 499

def invalid?(*args)
  !valid?(*args)
end

#ldap_ancestorsObject



365
366
367
# File 'lib/ldaptic/entry.rb', line 365

def ldap_ancestors
  self.class.ldap_ancestors | objectClass.map {|c|namespace.object_class(c)}
end

#loggerObject



206
207
208
# File 'lib/ldaptic/entry.rb', line 206

def logger
  self.class.logger
end

#may(include_aliases = false) ⇒ Object



385
386
387
388
389
390
391
392
393
394
395
# File 'lib/ldaptic/entry.rb', line 385

def may(include_aliases = false)
  attrs = (self.class.may + aux.map {|a| a.may(false)}.flatten).uniq - must
  if include_aliases
    attrs.map do |attr|
      type = namespace.attribute_type(attr)
      type ? type.names : attr
    end.flatten
  else
    attrs
  end
end

#may_must(attribute) ⇒ Object



397
398
399
400
401
402
403
404
# File 'lib/ldaptic/entry.rb', line 397

def may_must(attribute)
  attribute = Ldaptic.encode(attribute)
  if must(true).include?(attribute)
    :must
  elsif may(true).include?(attribute)
    :may
  end
end

#merge_attributes(data) ⇒ Object Also known as: attributes=



186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/ldaptic/entry.rb', line 186

def merge_attributes(data)
  # If it's a HashWithIndifferentAccess (eg, params in Rails), convert it
  # to a Hash with symbolic keys.  This causes the underscore/hyphen
  # translation to take place in write_attribute.  Form helpers in Rails
  # use a method name to read data,
  if defined?(::HashWithIndifferentAccess) && data.is_a?(HashWithIndifferentAccess)
    data = data.symbolize_keys
  end
  data.each do |key, value|
    write_attribute(key, value)
  end
end

#modify_attributes(mods) ⇒ Object

Commit an array of modifications directly to LDAP, without updating the local object.



338
339
340
341
# File 'lib/ldaptic/entry.rb', line 338

def modify_attributes(mods) #:nodoc:
  namespace.modify(dn, mods)
  self
end

#must(include_aliases = false) ⇒ Object



373
374
375
376
377
378
379
380
381
382
383
# File 'lib/ldaptic/entry.rb', line 373

def must(include_aliases = false)
  attrs = (self.class.must + aux.map {|a| a.must(false)}.flatten).uniq
  if include_aliases
    attrs.map do |attr|
      type = namespace.attribute_type(attr)
      type ? type.names : attr
    end.flatten
  else
    attrs
  end
end

#namespaceObject

A link back to the namespace.



202
203
204
# File 'lib/ldaptic/entry.rb', line 202

def namespace
  @namespace || self.class.namespace
end

#parentObject

The parent object containing this one.



233
234
235
236
237
238
239
# File 'lib/ldaptic/entry.rb', line 233

def parent
  unless @parent
    @parent = search(:base => dn.parent, :scope => :base, :limit => true)
    @parent.instance_variable_get(:@children)[rdn] = self
  end
  @parent
end

#persisted?Boolean

Has the object been saved before?

Returns:

  • (Boolean)


484
485
486
# File 'lib/ldaptic/entry.rb', line 484

def persisted?
  !!@original_attributes
end

#rdnObject

The first (relative) component of the distinguished name.



218
219
220
# File 'lib/ldaptic/entry.rb', line 218

def rdn
  dn && dn.rdn
end

#reloadObject

Refetches the attributes from the server.



550
551
552
553
554
555
556
557
# File 'lib/ldaptic/entry.rb', line 550

def reload
  new = search(:scope => :base, :limit => true)
  @original_attributes = new.instance_variable_get(:@original_attributes)
  @attributes          = new.instance_variable_get(:@attributes)
  @dn                  = Ldaptic::DN(new.dn, self)
  @children            = {}
  self
end

#rename(new_rdn, delete_old = nil) ⇒ Object



573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
# File 'lib/ldaptic/entry.rb', line 573

def rename(new_rdn, delete_old = nil)
  old_rdn = rdn
  if new_rdn.kind_of?(Ldaptic::DN)
    new_root = new_rdn.parent
    new_rdn = new_rdn.rdn
  else
    new_rdn = Ldaptic::RDN(new_rdn)
    new_root = nil
  end
  if delete_old.nil?
    delete_old = (new_rdn == old_rdn)
  end
  namespace.rename(dn, new_rdn.to_str, delete_old, *[new_root].compact)
  if delete_old
    old_rdn.each do |k, v|
      [@attributes, @original_attributes].each do |hash|
        hash.delete_if {|k2, v2| k.to_s.downcase == k2.to_s.downcase && v.to_s.downcase == v2.to_s.downcase }
        end
    end
  end
  old_dn = Ldaptic::DN(@dn, self)
  @dn = nil
  if new_root
    self.dn = new_root / new_rdn
  else
    self.dn = old_dn.parent / new_rdn
  end
  write_attributes_from_rdn(rdn, @original_attributes)
  if @parent
    children = @parent.instance_variable_get(:@children)
    if child = children.delete(old_rdn)
      children[new_rdn] = child if child == self
    end
  end
  self
end

#replace!(key, *values) ⇒ Object

:nodoc:



347
348
349
# File 'lib/ldaptic/entry.rb', line 347

def replace!(key, *values) #:nodoc:
  modify_attribute(:replace, key, values)
end

#respond_to?(method) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


406
407
408
409
# File 'lib/ldaptic/entry.rb', line 406

def respond_to?(method, *) #:nodoc:
  both = may(true) + must(true)
  super || (both + both.map {|x| "#{x}="} + both.map {|x| "#{x}-before-type-cast"}).include?(Ldaptic.encode(method.to_sym))
end

#saveObject

For new objects, does an LDAP add. For existing objects, does an LDAP modify. This only sends the modified attributes to the server. If a server constraint was violated, populates #errors and returns false.



520
521
522
523
524
525
526
527
528
529
530
# File 'lib/ldaptic/entry.rb', line 520

def save
  return false unless valid?
  if persisted?
    namespace.modify(dn, changes)
  else
    namespace.add(dn, changes)
  end
  @original_attributes = (@original_attributes||{}).merge(@attributes)
  @attributes = {}
  true
end

#save!Object

Like #save, but raise an exception if the entry could not be saved.



533
534
535
# File 'lib/ldaptic/entry.rb', line 533

def save!
  save ? self : raise(EntryNotSaved)
end

#search(options, &block) ⇒ Object

Searches for children. This is identical to Ldaptic::Base#search, only the default base is the current object’s DN.



439
440
441
442
443
444
# File 'lib/ldaptic/entry.rb', line 439

def search(options, &block)
  if options[:base].kind_of?(Hash)
    options = options.merge(:base => dn/options[:base])
  end
  namespace.search({:base => dn}.merge(options), &block)
end

#to_keyObject

Returns an array containing the DN. For ActiveModel compatibility.



223
224
225
# File 'lib/ldaptic/entry.rb', line 223

def to_key
  [dn] if persisted?
end

#to_modelObject

Returns self. For ActiveModel compatibility.



211
212
213
# File 'lib/ldaptic/entry.rb', line 211

def to_model
  self
end

#to_paramObject

Returns the DN. For ActiveModel compatibility.



228
229
230
# File 'lib/ldaptic/entry.rb', line 228

def to_param
  dn if persisted?
end

#to_sObject



264
265
266
# File 'lib/ldaptic/entry.rb', line 264

def to_s
  dn || ''
end

#update_attributes(hash) ⇒ Object

Assign the given attribute hash, then #save.



538
539
540
541
# File 'lib/ldaptic/entry.rb', line 538

def update_attributes(hash)
  merge_attributes(hash)
  save
end

#update_attributes!(hash) ⇒ Object

Like #update_attributes but raise on failure.



544
545
546
547
# File 'lib/ldaptic/entry.rb', line 544

def update_attributes!(hash)
  merge_attributes(hash)
  save!
end

#valid?Boolean

Returns:

  • (Boolean)


492
493
494
495
496
# File 'lib/ldaptic/entry.rb', line 492

def valid?
  errors.clear
  check_server_constraints
  errors.empty?
end