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 they 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
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.
-
.find_by_dn(dn) ⇒ Object
returns entry whose distinguised name is dn.
-
.hydrate(net_ldap_entry) ⇒ Object
Hydrates (populates) the object with values from the ldap resultset.
-
.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
- .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
-
#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(dn = nil) ⇒ 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
- #new_record? ⇒ Boolean
-
#setter_method?(method) ⇒ Boolean
returns
true
if method is a “setter”, i.e., ends in “=”. - #tainted_attributes ⇒ Object
-
#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(dn = nil) ⇒ 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.
85 86 87 88 89 |
# File 'lib/ucb_ldap_entry.rb', line 85 def initialize(dn = nil) #:nodoc: @new_record = true @attributes = {} @tainted_attributes = {} 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.
172 173 174 175 176 |
# File 'lib/ucb_ldap_entry.rb', line 172 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.
351 352 353 |
# File 'lib/ucb_ldap_entry.rb', line 351 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
273 274 275 |
# File 'lib/ucb_ldap_entry.rb', line 273 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.
235 236 237 238 239 240 241 |
# File 'lib/ucb_ldap_entry.rb', line 235 def create(args) args[:attributes][:objectclass] = object_classes net_ldap.add(args) or return false # why is the object being refetched from ldap here? find_by_dn(args[:dn]) end |
.create!(args) ⇒ Object
Same as #create(), but raises DirectoryNotUpdated on failure.
257 258 259 |
# File 'lib/ucb_ldap_entry.rb', line 257 def create!(args) create(args) || raise(DirectoryNotUpdatedException) end |
.entity_name ⇒ Object
Schema entity name. Set in each subclass.
363 364 365 |
# File 'lib/ucb_ldap_entry.rb', line 363 def entity_name @entity_name end |
.find_by_dn(dn) ⇒ Object
returns entry whose distinguised name is dn.
248 249 250 251 252 253 254 |
# File 'lib/ucb_ldap_entry.rb', line 248 def find_by_dn(dn) search( :base => dn, :scope => Net::LDAP::SearchScope_BaseObject, :filter => "objectClass=*" ).first end |
.hydrate(net_ldap_entry) ⇒ Object
Hydrates (populates) the object with values from the ldap resultset.
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/ucb_ldap_entry.rb', line 96 def self.hydrate(net_ldap_entry) new_ldap_entry = self.new new_ldap_entry.instance_variable_set(:@new_record, false) # 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. net_ldap_entry.each do |attr, value| new_ldap_entry.attributes[canonical(attr)] = value.map{|v| v.dup} end new_ldap_entry end |
.make_search_filter(filter) ⇒ Object
283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/ucb_ldap_entry.rb', line 283 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.
356 357 358 |
# File 'lib/ucb_ldap_entry.rb', line 356 def net_ldap #:nodoc: UCB::LDAP.net_ldap end |
.object_classes ⇒ Object
Returns Array
of object classes making up this type of LDAP entity.
296 297 298 |
# File 'lib/ucb_ldap_entry.rb', line 296 def object_classes @object_classes ||= UCB::LDAP::Schema.schema_hash[entity_name]["objectClasses"] end |
.required_attributes ⇒ Object
243 244 245 |
# File 'lib/ucb_ldap_entry.rb', line 243 def required_attributes schema_attributes_hash.delete_if {|key, value| value["required"] == false }.keys end |
.schema_attribute(attribute_name) ⇒ Object
317 318 319 320 |
# File 'lib/ucb_ldap_entry.rb', line 317 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.
305 306 307 308 |
# File 'lib/ucb_ldap_entry.rb', line 305 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.
312 313 314 315 |
# File 'lib/ucb_ldap_entry.rb', line 312 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'}
337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/ucb_ldap_entry.rb', line 337 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 << hydrate(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.
369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/ucb_ldap_entry.rb', line 369 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.
387 388 389 |
# File 'lib/ucb_ldap_entry.rb', line 387 def tree_base @tree_base end |
.tree_base=(tree_base) ⇒ Object
391 392 393 |
# File 'lib/ucb_ldap_entry.rb', line 391 def tree_base=(tree_base) @tree_base = tree_base end |
.unique_object_class ⇒ Object
300 301 302 |
# File 'lib/ucb_ldap_entry.rb', line 300 def unique_object_class @unique_object_class ||= UCB::LDAP::Schema.schema_hash[entity_name]["uniqueObjectClass"] end |
Instance Method Details
#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.
119 120 121 |
# File 'lib/ucb_ldap_entry.rb', line 119 def attributes @attributes end |
#canonical(string_or_symbol) ⇒ Object
:nodoc:
128 129 130 |
# File 'lib/ucb_ldap_entry.rb', line 128 def canonical(string_or_symbol) #:nodoc: self.class.canonical(string_or_symbol) end |
#delete ⇒ Object
delete entry. returns true
on sucess, false
on failure.
152 153 154 |
# File 'lib/ucb_ldap_entry.rb', line 152 def delete net_ldap.delete(:dn => dn) end |
#delete! ⇒ Object
same as #delete() except raises directorynotupdated on failure.
157 158 159 |
# File 'lib/ucb_ldap_entry.rb', line 157 def delete! delete || raise(directorynotupdatedexception) end |
#dn ⇒ Object
returns the value of the distinguished name attribute.
124 125 126 |
# File 'lib/ucb_ldap_entry.rb', line 124 def dn attributes[canonical(:dn)] end |
#modify ⇒ Object
207 208 209 210 211 212 213 |
# File 'lib/ucb_ldap_entry.rb', line 207 def modify() if ucb::ldap.net_ldap.modify(:dn => dn, :operations => modify_operations) @tainted_attributes = nil return true end false end |
#modify_operations ⇒ Object
197 198 199 200 201 202 203 204 205 |
# File 'lib/ucb_ldap_entry.rb', line 197 def modify_operations ops = [] tainted_attributes.keys.sort_by{|k| k.to_s}.each do |key| value = tainted_attributes[key] op = value.nil? ? :delete : :replace ops << [op, key, value] end ops end |
#net_ldap ⇒ Object
161 162 163 |
# File 'lib/ucb_ldap_entry.rb', line 161 def net_ldap self.class.net_ldap end |
#new_record? ⇒ Boolean
91 92 93 |
# File 'lib/ucb_ldap_entry.rb', line 91 def new_record? @new_record end |
#setter_method?(method) ⇒ Boolean
returns true
if method is a “setter”, i.e., ends in “=”.
179 180 181 |
# File 'lib/ucb_ldap_entry.rb', line 179 def setter_method?(method) method.to_s[-1, 1] == "=" end |
#tainted_attributes ⇒ Object
108 109 110 |
# File 'lib/ucb_ldap_entry.rb', line 108 def tainted_attributes @tainted_attributes 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)
137 138 139 140 141 142 143 144 |
# File 'lib/ucb_ldap_entry.rb', line 137 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.
147 148 149 |
# File 'lib/ucb_ldap_entry.rb', line 147 def update_attributes!(attrs) update_attributes(attrs) || raise(directorynotupdatedexception) end |
#value_getter(method) ⇒ Object
called by method_missing() to get an attribute value.
184 185 186 187 188 |
# File 'lib/ucb_ldap_entry.rb', line 184 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.
191 192 193 194 195 |
# File 'lib/ucb_ldap_entry.rb', line 191 def value_setter(method, *args) schema_attribute = self.class.schema_attribute(method.to_s.chop) attr_key = canonical(schema_attribute.name) tainted_attributes[attr_key] = schema_attribute.ldap_value(args[0]) end |