Class: UCB::LDAP::Entry
- Inherits:
-
Object
- Object
- UCB::LDAP::Entry
- Defined in:
- lib/ucb_ldap/entry.rb
Overview
UCB::LDAP::Entry
Abstract class representing an entry in the UCB LDAP directory. You won’t ever deal with Entry instances, but instead instances of Entry sub-classes.
Accessing LDAP Attributes
You will not see the attributes documented in the instance method section of the documentation for Entry sub-classes, even though you can access them as if they were instance methods.
person = Person.find_by_uid("123") #=> #<UCB::LDAP::Person ..>
people.givenname #=> ["John"]
Entry sub-classes may have convenience methods that allow for accessing attributes by friendly names:
person = Person.person_by_uid("123") #=> #<UCB::LDAP::Person ..>
person.firstname #=> "John"
See the sub-class documentation for specifics.
Single- / Multi-Value Attributes
Attribute values are returned as arrays or scalars based on how they are defined in the LDAP schema.
Entry subclasses may have convenience methods that return scalars even though the schema defines the unerlying attribute as multi-valued becuase in practice the are single-valued.
Attribute Types
Attribute values are stored as arrays of strings in LDAP, but when accessed through Entry sub-class methods are returned cast to their Ruby type as defined in the schema. Types are one of:
-
string
-
integer
-
boolean
-
datetime
Missing Attribute Values
If an attribute value is not present, the value returned depends on type and multi/single value field:
-
empty multi-valued attributes return an empty array ([])
-
empty booleans return
false
-
everything else returns
nil
if empty
Attempting to get or set an attribute value for an invalid attriubte name will raise a BadAttributeNameException.
Updating LDAP
If your bind has privleges for updating the directory you can update the directory using methods of Entry sub-classes. Make sure you call UCB::LDAP.authenticate before calling any update methods.
There are three pairs of update methods that behave like Rails ActiveRecord methods of the same name. These methods are fairly thin wrappers around standard LDAP update commands.
The “bang” methods (those ending in “!”) differ from their bangless counterparts in that the bang methods raise DirectoryNotUpdatedException
on failure, while the bangless return false
.
-
#create/#create! - class methods that do LDAP add
-
#update_attributes/#update_attributes! - instance methods that do LDAP modify
-
#delete/#delete! - instance methods that do LDAP delete
Direct Known Subclasses
Address, Affiliation, JobAppointment, Namespace, Org, Person, Service, StudentTerm
Constant Summary collapse
- TESTING =
false
Class Method Summary collapse
-
.canonical(string_or_symbol) ⇒ Object
Returns the canonical representation of a symbol or string so we can look up attributes in a number of ways.
-
.combine_filters(filters, operator = '&') ⇒ Object
Returns a new Net::LDAP::Filter that is the result of combining filters using operator (filters is an
Array
of Net::LDAP::Filter). -
.create(args) ⇒ Object
Creates and returns new entry.
-
.create!(args) ⇒ Object
Same as #create(), but raises DirectoryNotUpdated on failure.
-
.entity_name ⇒ Object
Schema entity name.
- .filter_in(attribute_name, array_of_values) ⇒ Object
-
.find_by_dn(dn) ⇒ Object
Returns entry whose distinguised name is dn.
-
.make_search_filter(filter) ⇒ Object
Returns Net::LDAP::Filter.
-
.net_ldap ⇒ Object
Returns underlying Net::LDAP instance.
-
.object_classes ⇒ Object
Returns
Array
of object classes making up this type of LDAP entity. -
.required_attributes ⇒ Object
returns an Array of symbols where each symbol is the name of a required attribute for the Entry.
-
.required_schema_attributes ⇒ Object
returns Hash of SchemaAttribute objects that are required for the Entry.
- .schema_attribute(attribute_name) ⇒ Object
-
.schema_attributes_array ⇒ Object
Returns an
Array
of Schema::Attribute for the entity. -
.schema_attributes_hash ⇒ Object
Returns as
Hash
whose keys are the canonical attribute names and whose values are the corresponding Schema::Attributes. -
.search(args = {}) ⇒ Object
Returns Array of UCB::LDAP::Entry for entries matching args.
-
.set_schema_attributes ⇒ Object
Want an array of Schema::Attributes as well as a hash of all possible variations on a name pointing to correct array element.
-
.tree_base ⇒ Object
Returns tree base for LDAP searches.
- .tree_base=(tree_base) ⇒ Object
- .unique_object_class ⇒ Object
Instance Method Summary collapse
- #assigned_attributes ⇒ Object
-
#attributes ⇒ Object
Hash
of attributes returned from underlying NET::LDAP::Entry instance. -
#canonical(string_or_symbol) ⇒ Object
:nodoc:.
-
#delete ⇒ Object
Delete entry.
-
#delete! ⇒ Object
Same as #delete() except raises DirectoryNotUpdated on failure.
-
#dn ⇒ Object
Returns the value of the Distinguished Name attribute.
-
#initialize(net_ldap_entry) ⇒ Entry
constructor
Returns new instance of UCB::LDAP::Entry.
-
#method_missing(method, *args) ⇒ Object
Used to get/set attribute values.
- #modify ⇒ Object
- #modify_operations ⇒ Object
- #net_ldap ⇒ Object
-
#setter_method?(method) ⇒ Boolean
Returns
true
if method is a “setter”, i.e., ends in “=”. -
#update_attributes(attrs) ⇒ Object
Update an existing entry.
-
#update_attributes!(attrs) ⇒ Object
Same as #update_attributes(), but raises DirectoryNotUpdated on failure.
-
#value_getter(method) ⇒ Object
Called by method_missing() to get an attribute value.
-
#value_setter(method, *args) ⇒ Object
Called by method_missing() to set an attribute value.
Constructor Details
#initialize(net_ldap_entry) ⇒ Entry
Returns new instance of UCB::LDAP::Entry. The argument net_ldap_entry is an instance of Net::LDAP::Entry.
You should not need to create any UCB::LDAP::Entry instances; they are created by calls to UCB::LDAP.search and friends.
88 89 90 91 92 93 94 95 96 |
# File 'lib/ucb_ldap/entry.rb', line 88 def initialize(net_ldap_entry) #:nodoc: # Don't store Net::LDAP entry in object since it uses the block # initialization method of Hash which can't be marshalled ... this # means it can't be stored in a Rails session. @attributes = {} net_ldap_entry.each do |attr, value| @attributes[canonical(attr)] = value.map { |v| v.dup } end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args) ⇒ Object
Used to get/set attribute values.
If we can’t make an attribute name out of method, let regular method_missing() handle it.
171 172 173 174 175 |
# File 'lib/ucb_ldap/entry.rb', line 171 def method_missing(method, *args) #:nodoc: setter_method?(method) ? value_setter(method, *args) : value_getter(method) rescue BadAttributeNameException return super end |
Class Method Details
.canonical(string_or_symbol) ⇒ Object
Returns the canonical representation of a symbol or string so we can look up attributes in a number of ways.
399 400 401 |
# File 'lib/ucb_ldap/entry.rb', line 399 def canonical(string_or_symbol) string_or_symbol.to_s.downcase.to_sym end |
.combine_filters(filters, operator = '&') ⇒ Object
Returns a new Net::LDAP::Filter that is the result of combining filters using operator (filters is an Array
of Net::LDAP::Filter).
See Net::LDAP#& and Net::LDAP#| for details.
f1 = Net::LDAP::Filter.eq("lastname", "hansen")
f2 = Net::LDAP::Filter.eq("firstname", "steven")
combine_filters([f1, f2]) # same as: f1 & f2
combine_filters([f1, f2], '|') # same as: f1 | f2
285 286 287 |
# File 'lib/ucb_ldap/entry.rb', line 285 def combine_filters(filters, operator = '&') filters.inject { |accum, filter| accum.send(operator, filter) } end |
.create(args) ⇒ Object
Creates and returns new entry. Returns false
if unsuccessful. Sets :objectclass key of args[:attributes] to object_classes read from schema.
dn = "uid=999999,ou=people,dc=example,dc=com"
attr = {
:uid => "999999",
:mail => "[email protected]"
}
EntrySubClass.create(:dn => dn, :attributes => attr) #=> #<UCB::LDAP::EntrySubClass ..>
Caller is responsible for setting :dn and :attributes correctly, as well as any other validation.
249 250 251 252 253 254 |
# File 'lib/ucb_ldap/entry.rb', line 249 def create(args) args[:attributes][:objectclass] = object_classes result = net_ldap.add(args) result or return false find_by_dn(args[:dn]) end |
.create!(args) ⇒ Object
Same as #create(), but raises DirectoryNotUpdated on failure.
268 269 270 |
# File 'lib/ucb_ldap/entry.rb', line 268 def create!(args) create(args) || raise(DirectoryNotUpdatedException) end |
.entity_name ⇒ Object
Schema entity name. Set in each subclass.
416 417 418 |
# File 'lib/ucb_ldap/entry.rb', line 416 def entity_name @entity_name end |
.filter_in(attribute_name, array_of_values) ⇒ Object
229 230 231 232 |
# File 'lib/ucb_ldap/entry.rb', line 229 def filter_in(attribute_name, array_of_values) filters = array_of_values.map { |value| Net::LDAP::Filter.eq(attribute_name, value) } UCB::LDAP::Entry.combine_filters(filters, '|') end |
.find_by_dn(dn) ⇒ Object
Returns entry whose distinguised name is dn.
258 259 260 261 262 263 264 |
# File 'lib/ucb_ldap/entry.rb', line 258 def find_by_dn(dn) search( :base => dn, :scope => Net::LDAP::SearchScope_BaseObject, :filter => "objectClass=*" ).first end |
.make_search_filter(filter) ⇒ Object
296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/ucb_ldap/entry.rb', line 296 def make_search_filter(filter) return filter if filter.instance_of? Net::LDAP::Filter return filter if filter.instance_of? String filters = [] # sort so result is predictable for unit test filter.keys.sort_by { |symbol| "#{symbol}" }.each do |attr| filters << Net::LDAP::Filter.eq("#{attr}", "#{filter[attr]}") end combine_filters(filters, "&") end |
.net_ldap ⇒ Object
Returns underlying Net::LDAP instance.
406 407 408 |
# File 'lib/ucb_ldap/entry.rb', line 406 def net_ldap #:nodoc: UCB::LDAP.net_ldap end |
.object_classes ⇒ Object
Returns Array
of object classes making up this type of LDAP entity.
310 311 312 |
# File 'lib/ucb_ldap/entry.rb', line 310 def object_classes @object_classes ||= UCB::LDAP::Schema.schema_hash[entity_name]["objectClasses"] end |
.required_attributes ⇒ Object
returns an Array of symbols where each symbol is the name of a required attribute for the Entry
321 322 323 |
# File 'lib/ucb_ldap/entry.rb', line 321 def required_attributes required_schema_attributes.keys end |
.required_schema_attributes ⇒ Object
returns Hash of SchemaAttribute objects that are required for the Entry. Each SchemaAttribute object is keyed to the attribute’s name.
Note: required_schema_attributes will not return aliases, it only returns the original attributes
Example:
Person.required_schema_attribues[:cn]
=> <UCB::LDAP::Schema::Attribute:0x11c6b68>
337 338 339 340 341 342 343 |
# File 'lib/ucb_ldap/entry.rb', line 337 def required_schema_attributes required_atts = schema_attributes_hash.reject { |key, value| !value.required? } required_atts.reject do |key, value| aliases = value.aliases.map { |a| canonical(a) } aliases.include?(key) end end |
.schema_attribute(attribute_name) ⇒ Object
362 363 364 365 |
# File 'lib/ucb_ldap/entry.rb', line 362 def schema_attribute(attribute_name) schema_attributes_hash[canonical(attribute_name)] || raise(BadAttributeNameException, "'#{attribute_name}' is not a recognized attribute name") end |
.schema_attributes_array ⇒ Object
Returns an Array
of Schema::Attribute for the entity.
348 349 350 351 |
# File 'lib/ucb_ldap/entry.rb', line 348 def schema_attributes_array @schema_attributes_array || set_schema_attributes @schema_attributes_array end |
.schema_attributes_hash ⇒ Object
Returns as Hash
whose keys are the canonical attribute names and whose values are the corresponding Schema::Attributes.
357 358 359 360 |
# File 'lib/ucb_ldap/entry.rb', line 357 def schema_attributes_hash @schema_attributes_hash || set_schema_attributes @schema_attributes_hash end |
.search(args = {}) ⇒ Object
Returns Array of UCB::LDAP::Entry for entries matching args. When called from a subclass, returns Array of subclass instances.
See Net::LDAP::search for more information on args.
Most common arguments are :base
and :filter
. Search methods of subclasses have default :base
that can be overriden.
See make_search_filter for :filter
options.
base = "ou=people,dc=berkeley,dc=edu"
entries = UCB::LDAP::Entry.search(:base => base, :filter => {:uid => '123'})
entries = UCB::LDAP::Entry.search(:base => base, :filter => {:sn => 'Doe', :givenname => 'John'}
383 384 385 386 387 388 389 390 391 392 393 |
# File 'lib/ucb_ldap/entry.rb', line 383 def search(args={}) args = args.dup args[:base] ||= tree_base args[:filter] = make_search_filter args[:filter] if args[:filter] results = [] net_ldap.search(args) do |entry| results << new(entry) end results end |
.set_schema_attributes ⇒ Object
Want an array of Schema::Attributes as well as a hash of all possible variations on a name pointing to correct array element.
424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/ucb_ldap/entry.rb', line 424 def set_schema_attributes @schema_attributes_array = [] @schema_attributes_hash = {} UCB::LDAP::Schema.schema_hash[entity_name]["attributes"].each do |k, v| sa = UCB::LDAP::Schema::Attribute.new(v.merge("name" => k)) @schema_attributes_array << sa [sa.name, sa.aliases].flatten.each do |name| @schema_attributes_hash[canonical(name)] = sa end end rescue raise "Error loading schema attributes for entity_name '#{entity_name}'" end |
.tree_base ⇒ Object
Returns tree base for LDAP searches. Subclasses each have their own value.
Can be overridden in #search by passing in a :base
parm.
444 445 446 |
# File 'lib/ucb_ldap/entry.rb', line 444 def tree_base @tree_base end |
.tree_base=(tree_base) ⇒ Object
448 449 450 |
# File 'lib/ucb_ldap/entry.rb', line 448 def tree_base=(tree_base) @tree_base = tree_base end |
.unique_object_class ⇒ Object
314 315 316 |
# File 'lib/ucb_ldap/entry.rb', line 314 def unique_object_class @unique_object_class ||= UCB::LDAP::Schema.schema_hash[entity_name]["uniqueObjectClass"] end |
Instance Method Details
#assigned_attributes ⇒ Object
202 203 204 |
# File 'lib/ucb_ldap/entry.rb', line 202 def assigned_attributes @assigned_attributes ||= {} end |
#attributes ⇒ Object
Hash
of attributes returned from underlying NET::LDAP::Entry instance. Hash keys are #canonical attribute names, hash values are attribute values as returned from LDAP, i.e. arrays.
You should most likely be referencing attributes as if they were instance methods rather than directly through this method. See top of this document.
107 108 109 |
# File 'lib/ucb_ldap/entry.rb', line 107 def attributes @attributes end |
#canonical(string_or_symbol) ⇒ Object
:nodoc:
118 119 120 |
# File 'lib/ucb_ldap/entry.rb', line 118 def canonical(string_or_symbol) #:nodoc: self.class.canonical(string_or_symbol) end |
#delete ⇒ Object
Delete entry. Returns true
on sucess, false
on failure.
147 148 149 |
# File 'lib/ucb_ldap/entry.rb', line 147 def delete net_ldap.delete(:dn => dn) end |
#delete! ⇒ Object
Same as #delete() except raises DirectoryNotUpdated on failure.
154 155 156 |
# File 'lib/ucb_ldap/entry.rb', line 154 def delete! delete || raise(DirectoryNotUpdatedException) end |
#dn ⇒ Object
Returns the value of the Distinguished Name attribute.
114 115 116 |
# File 'lib/ucb_ldap/entry.rb', line 114 def dn attributes[canonical(:dn)] end |
#modify ⇒ Object
216 217 218 219 220 221 222 |
# File 'lib/ucb_ldap/entry.rb', line 216 def modify() if UCB::LDAP.net_ldap.modify(:dn => dn, :operations => modify_operations) @assigned_attributes = nil return true end false end |
#modify_operations ⇒ Object
206 207 208 209 210 211 212 213 214 |
# File 'lib/ucb_ldap/entry.rb', line 206 def modify_operations ops = [] assigned_attributes.keys.sort_by { |k| k.to_s }.each do |key| value = assigned_attributes[key] op = value.nil? ? :delete : :replace ops << [op, key, value] end ops end |
#net_ldap ⇒ Object
158 159 160 |
# File 'lib/ucb_ldap/entry.rb', line 158 def net_ldap self.class.net_ldap end |
#setter_method?(method) ⇒ Boolean
Returns true
if method is a “setter”, i.e., ends in “=”.
180 181 182 |
# File 'lib/ucb_ldap/entry.rb', line 180 def setter_method?(method) method.to_s[-1, 1] == "=" end |
#update_attributes(attrs) ⇒ Object
Update an existing entry. Returns entry if successful else false.
attrs = {:attr1 => "new_v1", :attr2 => "new_v2"}
entry.update_attributes(attrs)
128 129 130 131 132 133 134 135 |
# File 'lib/ucb_ldap/entry.rb', line 128 def update_attributes(attrs) attrs.each { |k, v| self.send("#{k}=", v) } if modify @attributes = self.class.find_by_dn(dn).attributes.dup return true end false end |
#update_attributes!(attrs) ⇒ Object
Same as #update_attributes(), but raises DirectoryNotUpdated on failure.
140 141 142 |
# File 'lib/ucb_ldap/entry.rb', line 140 def update_attributes!(attrs) update_attributes(attrs) || raise(DirectoryNotUpdatedException) end |
#value_getter(method) ⇒ Object
Called by method_missing() to get an attribute value.
187 188 189 190 191 |
# File 'lib/ucb_ldap/entry.rb', line 187 def value_getter(method) schema_attribute = self.class.schema_attribute(method) raw_value = attributes[canonical(schema_attribute.name)] schema_attribute.get_value(raw_value) end |
#value_setter(method, *args) ⇒ Object
Called by method_missing() to set an attribute value.
196 197 198 199 200 |
# File 'lib/ucb_ldap/entry.rb', line 196 def value_setter(method, *args) schema_attribute = self.class.schema_attribute(method.to_s.chop) attr_key = canonical(schema_attribute.name) assigned_attributes[attr_key] = schema_attribute.ldap_value(args[0]) end |