Class: PassiveLDAP::Base
- Inherits:
-
Object
- Object
- PassiveLDAP::Base
- Includes:
- ActiveRecord::Aggregations, ActiveRecord::Associations, ActiveRecord::AttributeMethods, ActiveRecord::Callbacks, ActiveRecord::Locking::Optimistic, ActiveRecord::Observing, ActiveRecord::Reflection, ActiveRecord::Serialization, ActiveRecord::Timestamp, ActiveRecord::Validations
- Defined in:
- lib/passiveldap.rb
Overview
Base class. See the documentation of #passive_ldap and #passive_ldap_attr
Direct Known Subclasses
Constant Summary collapse
- VERSION =
"0.1"
- @@default_timezone =
:local
Class Method Summary collapse
-
.attr_mapfrom ⇒ Object
gets the attribute_passive_ldap_name=>attribute_ldap_server_name hash.
-
.attr_mapto ⇒ Object
gets the attribute_ldap_server_name=>attribute_passive_ldap_name hash.
-
.attrs ⇒ Object
gets the attributes hash set with #passive_ldap_attr (excluding hidden values).
-
.attrs_all ⇒ Object
gets the attributes hash set with #passive_ldap_attr (including hidden values).
-
.bind(password = nil, username = nil) ⇒ Object
Binds to the directory with the username and password given.
-
.column_names ⇒ Object
AR returns an array of the attribute names as strings (if mapped then it will return the mapped name).
-
.columns ⇒ Object
AR returns an array of the columns as ActiveRecord::ConnectionAdapters::Column.
-
.columns_hash ⇒ Object
AR returns a hash of column objects.
-
.content_columns ⇒ Object
AR return the array of column objects without the id column.
-
.count(*args) ⇒ Object
AR always returns the number of records.
-
.create(attributes = nil) ⇒ Object
AR Creates an object (or multiple objects) and saves it to the database, if validations pass.
-
.define_attr_method(name, value = nil, &block) ⇒ Object
AR Defines an “attribute” method (like #inheritance_column or #table_name).
-
.delete(id) ⇒ Object
AR deletes the record.
-
.delete_all(conditions = nil) ⇒ Object
AR not implemented.
-
.destroy(id) ⇒ Object
AR same as delete.
-
.destroy_all(conditions = nil) ⇒ Object
AR not implemented.
-
.exists?(id_or_filter) ⇒ Boolean
AR checks whether the given id, or an object that satisfies the given Net::LDAP::Filter exist in the directory.
-
.find(user, filter = nil) ⇒ Object
AR find a user defined by it’s ID and return the object.
-
.generated_methods ⇒ Object
AR Returns an array of the generated methods.
-
.generated_methods? ⇒ Boolean
AR Returns true - attribute methods are generated in initalize.
-
.human_attribute_name(attribute_key_name) ⇒ Object
AR returns a humanized attribute name.
-
.initialize_ldap_con ⇒ Object
creates a new Net::LDAP object.
-
.inspect ⇒ Object
AR returns a string like “
User id:integer name:string mail:text
” multi-valued attributes will be text. -
.passive_ldap(connection_attributes) ⇒ Object
sets the connection and record attributes that are used.
-
.passive_ldap_attr(attribs) ⇒ Object
Sets the attributes you would like to use.
-
.primary_key ⇒ Object
AR returns
:id
. -
.serialize(attr_name, class_name = Object) ⇒ Object
AR not implemented.
-
.serialized_attributes ⇒ Object
AR not implemented.
-
.settings ⇒ Object
gets the hash set with #passive_ldap.
-
.table_name ⇒ Object
AR will return the name of the class.
-
.update(id, attributes) ⇒ Object
AR Updates an object or objects (if passed an Array) with the attributes given.
-
.update_all(updates, conditions = nil, options = {}) ⇒ Object
AR not implemented.
-
.update_counters(id, counters) ⇒ Object
AR not implemented.
-
.validates_format_of_each(*attr_names) ⇒ Object
validates the format of each value in a multi-valued attribute.
Instance Method Summary collapse
-
#[](attribute) ⇒ Object
AR gets the value of the attribute.
-
#[]=(attribute, value) ⇒ Object
AR sets the value of the attribute.
-
#array_separator(new_sep = nil) ⇒ Object
sets the array_separator.
-
#attribute_names ⇒ Object
AR Returns an array of symbols of the attributes that can be changed; sorted alphabetically.
-
#attribute_present?(attribute) ⇒ Boolean
AR Returns true if the specified attribute has been set by the user or by a database load and is neither nil nor empty?.
-
#attributes(options = nil) ⇒ Object
AR Returns a hash of all the attributes with their names as keys and clones of their objects as values.
-
#attributes=(new_attributes, guard_protected_attribute = true) ⇒ Object
AR sets multiple attributes at once.
-
#bind(password = nil, username = nil) ⇒ Object
Bind to the directory to check whether the credentials are right or not.
-
#clone ⇒ Object
AR not implemented.
-
#column_for_attribute(name) ⇒ Object
AR returns the column object of the named attribute.
-
#destroy ⇒ Object
AR deletes the record in the directory and freezes the object.
-
#dn ⇒ Object
gets the distinguished name of the record.
-
#dn=(newdn) ⇒ Object
sets the distinguished name of the record.
-
#exists_in_directory ⇒ Object
returns whether the record is new, or it is originated from the directory.
-
#get_attribute(attribute) ⇒ Object
returns the attrbiute.
-
#get_old_attribute(attribute) ⇒ Object
gets the original value (the value that was read from the directory, or nil if this is a new record) of an attribute.
-
#id ⇒ Object
AR gets the id of the record.
-
#id=(a) ⇒ Object
AR sets the id of the record.
-
#id? ⇒ Boolean
gets whether the id is set.
-
#initialize(userid = nil) {|_self| ... } ⇒ Base
constructor
AR create a record object and populate it’s data from the LDAP directory.
-
#inspect ⇒ Object
AR Returns the contents of the record as a string.
-
#new_record? ⇒ Boolean
AR Returns true if this object hasn’t been saved yet - that is, a record for the object doesn’t exist in the directory yet.
-
#reload(options = nil) ⇒ Object
AR reloads the data from the directory.
-
#respond_to_without_attributes?(method, include_priv = false) ⇒ Boolean
AR needed by ActiveRecord::Callbacks.
-
#save ⇒ Object
AR Saves the changes back to the LDAP server.
-
#save! ⇒ Object
AR saves the record but will raise a RecordNotSaved with the cause of the failure if unsuccesful.
-
#set_attribute(attribute, value, raise_error_when_readonly = false) ⇒ Object
sets the attribute.
-
#set_password(newpass, method, options = nil) ⇒ Object
changes the password of the record.
-
#set_password!(newpass, method, options = nil) ⇒ Object
same as set_password but will raise a RecordNotSaved exception in unsuccesful.
-
#set_protection_level(level = 0, password = nil, username = nil) ⇒ Object
Attributes may have different protection levels.
-
#to_s ⇒ Object
returns the user id as string.
-
#update_attribute(name, value) ⇒ Object
AR updates a single attribute and saves the record.
-
#update_attributes(attributes) ⇒ Object
AR updates multiple attributes and saves the record.
-
#update_attributes!(attributes) ⇒ Object
AR see update_attributes.
Constructor Details
#initialize(userid = nil) {|_self| ... } ⇒ Base
AR create a record object and populate it’s data from the LDAP directory. If the record is not found it will create an empty user with that id
Beware! If userid is nil it will try to guess a new id number using the Proc in #passive_ldap. By default this guess is not guaranteed to be unique in a multi-threaded application. See #passive_ldap
the parameter may be a Hash with attributes that are the initial values.
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 |
# File 'lib/passiveldap.rb', line 669 def initialize(userid = nil) values = nil if userid.kind_of?(Hash) values = userid.clone values[:id] ||= nil userid = values[:id] end raise ARFeatureMissing, "Id must be a Hash or a number" unless userid.kind_of?(Integer) or (userid.kind_of?(String) and userid.to_i.to_s == userid) or userid.nil? userid = self.class.settings[:new_id].call(self) if userid.nil? @array_separator = self.class.settings[:default_array_separator] @protection_level = 0 @protection_username = nil @protection_password = nil @generated_methods = Set.new @dn = nil self.class.attrs.each { |name,value| alt_name = value[:name] eval "@#{alt_name.to_s} = nil" if not self.class.method_defined?(alt_name) then self.class.module_eval <<-EOF def #{alt_name.id2name} read_mapped_attribute(:#{alt_name.to_s}) end def #{alt_name.id2name}=(a) write_mapped_attribute(:#{alt_name.to_s},a) end def #{alt_name.id2name}? if @attributes.has_key?(:#{name.to_s}) then unless @attributes[:#{name.to_s}].nil? or @attributes[:#{name.to_s}] == "" or @attributes[:#{name.to_s}] == [] then true else false end else false end end EOF @generated_methods << "#{alt_name.id2name}".to_sym @generated_methods << "#{alt_name.id2name}=".to_sym @generated_methods << "#{alt_name.id2name}?".to_sym end } reload(:id => userid) @errors = ActiveRecord::Errors.new(self) unless values.nil? values[:id] = userid values.each { |key,value| write_mapped_attribute(key,value) unless key == :id } self.id = userid end yield self if block_given? end |
Class Method Details
.attr_mapfrom ⇒ Object
gets the attribute_passive_ldap_name=>attribute_ldap_server_name hash
174 175 176 |
# File 'lib/passiveldap.rb', line 174 def attr_mapfrom read_inheritable_attribute(:mapfrom) end |
.attr_mapto ⇒ Object
gets the attribute_ldap_server_name=>attribute_passive_ldap_name hash
169 170 171 |
# File 'lib/passiveldap.rb', line 169 def attr_mapto read_inheritable_attribute(:mapto) end |
.attrs ⇒ Object
gets the attributes hash set with #passive_ldap_attr (excluding hidden values)
159 160 161 |
# File 'lib/passiveldap.rb', line 159 def attrs read_inheritable_attribute(:attrs) end |
.attrs_all ⇒ Object
gets the attributes hash set with #passive_ldap_attr (including hidden values)
164 165 166 |
# File 'lib/passiveldap.rb', line 164 def attrs_all read_inheritable_attribute(:attr_orig) end |
.bind(password = nil, username = nil) ⇒ Object
Binds to the directory with the username and password given. Password may be a Proc object, see the documentation of Net::LDAP#bind
Will return true if the bind is sucesful, and will raise a ConnectionError with the message returned from the server if the bind fails
If password and username is nil, bind will try to bind with the default connection parameters
Beware! Password is the first parameter!
187 188 189 190 191 192 193 |
# File 'lib/passiveldap.rb', line 187 def bind(password = nil, username = nil) ldap = initialize_ldap_con ldap.authenticate(username,password) if password ldap.bind raise ConnectionError, ldap.get_operation_result. unless ldap.get_operation_result.code == 0 true end |
.column_names ⇒ Object
AR returns an array of the attribute names as strings (if mapped then it will return the mapped name)
221 222 223 224 225 226 227 228 229 |
# File 'lib/passiveldap.rb', line 221 def column_names unless @column_names @column_names = ["id"] attrs.each { |key,value| @column_names << value[:name].to_s if key != settings[:id_attribute] } end @column_names end |
.columns ⇒ Object
AR returns an array of the columns as ActiveRecord::ConnectionAdapters::Column
The id is ‘int(8)’ the multi-valued attributes are ‘text’, all others are ‘varchar’
234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/passiveldap.rb', line 234 def columns unless @columns @columns = self.column_names.collect { |e| if e == "id" then i = ActiveRecord::ConnectionAdapters::Column.new("id",'0','int(8)',false) i.primary = true else i = ActiveRecord::ConnectionAdapters::Column.new(e,'',attrs[attr_mapfrom[e.to_sym]][:multi_valued]?'text':'varchar',true) end i } end @columns end |
.columns_hash ⇒ Object
AR returns a hash of column objects. See columns
250 251 252 253 254 255 256 257 258 259 |
# File 'lib/passiveldap.rb', line 250 def columns_hash unless @columns_hash a = self.columns @columns_hash = {} a.each { |e| @columns_hash[e.name] = e } end @columns_hash end |
.content_columns ⇒ Object
AR return the array of column objects without the id column
262 263 264 265 266 |
# File 'lib/passiveldap.rb', line 262 def content_columns a = columns a.delete_if { |e| e.name == "id" } a end |
.count(*args) ⇒ Object
AR always returns the number of records. Should be changed to something more intelligent
Doesn’t raise ARFeatureMissing yet
215 216 217 |
# File 'lib/passiveldap.rb', line 215 def count(*args) find(:all).length end |
.create(attributes = nil) ⇒ Object
AR Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
The attributes parameter can be either be a Hash or an Array of Hashes. These Hashes describe the attributes on the objects that are to be created.
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/passiveldap.rb', line 273 def create(attributes = nil) if attributes.nil? then a = new a.save a else attributes = [attributes] unless attributes.kind_of?(Array) c = [] attributes.each { |b| b[:id] ||= nil a = new(b[:id]) b.each { |key,value| if key!=:id then a[key] = value end } a.save c << a } if attributes.length==1 then c[0] else c end end end |
.define_attr_method(name, value = nil, &block) ⇒ Object
AR Defines an “attribute” method (like #inheritance_column or #table_name). A new (class) method will be created with the given name. If a value is specified, the new method will return that value (as a string). Otherwise, the given block will be used to compute the value of the method.
The original method will be aliased, with the new name being prefixed with “original_”. This allows the new method to access the original value.
Example:
class A < ActiveRecord::Base
define_attr_method :primary_key, "sysid"
define_attr_method( :inheritance_column ) do
original_inheritance_column + "_id"
end
end
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 |
# File 'lib/passiveldap.rb', line 1155 def define_attr_method(name, value=nil, &block) sing = class << self; self; end sing.send :alias_method, "original_#{name}", name if block_given? sing.send :define_method, name, &block else # use eval instead of a block to work around a memory leak in dev # mode in fcgi sing.class_eval "def #{name}; #{value.to_s.inspect}; end" end end |
.delete(id) ⇒ Object
AR deletes the record. Object will be instantiated
301 302 303 304 |
# File 'lib/passiveldap.rb', line 301 def delete(id) a = new(id) a.destroy end |
.delete_all(conditions = nil) ⇒ Object
AR not implemented. Raises ARMethodMissing
307 308 309 |
# File 'lib/passiveldap.rb', line 307 def delete_all(conditions = nil) raise ARMethodMissing, "ARMethodMissing: delete_all" end |
.destroy(id) ⇒ Object
AR same as delete
312 313 314 |
# File 'lib/passiveldap.rb', line 312 def destroy(id) delete(id) end |
.destroy_all(conditions = nil) ⇒ Object
AR not implemented. Raises ARMethodMissing
317 318 319 |
# File 'lib/passiveldap.rb', line 317 def destroy_all(conditions = nil) raise ARMethodMissing, "ARMethodMissing: destroy_all" end |
.exists?(id_or_filter) ⇒ Boolean
AR checks whether the given id, or an object that satisfies the given Net::LDAP::Filter exist in the directory
will throw ARFeatureMissing if id_or_filter is not an integer or a Filter
324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/passiveldap.rb', line 324 def exists?(id_or_filter) raise ARFeatureMissing, "id_or_filter must be an id or a filter" unless id_or_filter.kind_of?(Integer) or (id_or_filter.kind_of?(String) and id_or_filter.to_i.to_s == id_or_filter) or id_or_filter.kind_of?(Net::LDAP::Filter) begin if id_or_filter.kind_of?(Net::LDAP::Filter) then find(:first,id_or_filter) else find(id_or_filter) end rescue RecordNotFound return false end true end |
.find(user, filter = nil) ⇒ Object
AR find a user defined by it’s ID and return the object. If it is not found in the database it will raise RecordNotFound
If you pass the :all
symbol as parameter, it will return an array with all objects in the directory. If no object is found it will return an empty array
If you pass the :first
symbol as parameter, it will return the first object in the directory
the optional filter parameter is used to join a new filter to the default one. The filter parameter is only used in :all
and :first
searches
will throw ARFeatureMissing if passed a Hash or an Array instead of a Net::LDAP::Filter, or if the first parameter is not an id, or one the following symbols: :all
, :first
Currently it will allow Hash filters, if all of the Hash parameters are nil. This is because doing so belongs_to relations will work.
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
# File 'lib/passiveldap.rb', line 354 def find(user, filter = nil) raise ARFeatureMissing, "User must be a number, :all or :first. Supplied was #{filter.inspect}" unless user.kind_of?(Integer) or user == :all or user == :first or (user.kind_of?(String) and user.to_i.to_s == user) if filter.kind_of?(Hash) then testf = true filter.each { |key,value| testf = false unless value.nil? } filter = nil if testf end raise ARFeatureMissing, "Filter must be a Net::LDAP::Filter or nil. Supplied was #{filter.inspect}" unless filter.nil? or filter.kind_of?(Net::LDAP::Filter) #filter = nil unless filter.kind_of?(Net::LDAP::Filter) if user == :all or user == :first then a = [] ldap = self.initialize_ldap_con if filter then filter = filter & self.settings[:multiple_record_filter].call(self) else filter = self.settings[:multiple_record_filter].call(self) end alreadygot = false ldap.search( :return_result => false, :scope => self.settings[:record_scope], :base => self.settings[:record_base], :filter => filter ) do |entry| eval "a << self.new(entry.#{self.settings[:id_attribute].id2name}[0].to_i)" unless user == :first and alreadygot alreadygot = true end raise ConnectionError, ldap.get_operation_result. unless ldap.get_operation_result.code == 0 if user == :all then a elsif a == [] then raise PassiveLDAP::RecordNotFound else a[0] end else a = self.new(user) if a.exists_in_directory then a else raise PassiveLDAP::RecordNotFound end end end |
.generated_methods ⇒ Object
AR Returns an array of the generated methods
202 203 204 |
# File 'lib/passiveldap.rb', line 202 def generated_methods @generated_methods ||= Set.new end |
.generated_methods? ⇒ Boolean
AR Returns true - attribute methods are generated in initalize
207 208 209 |
# File 'lib/passiveldap.rb', line 207 def generated_methods? true end |
.human_attribute_name(attribute_key_name) ⇒ Object
AR returns a humanized attribute name
397 398 399 |
# File 'lib/passiveldap.rb', line 397 def human_attribute_name(attribute_key_name) attribute_key_name.humanize end |
.initialize_ldap_con ⇒ Object
creates a new Net::LDAP object
1102 1103 1104 |
# File 'lib/passiveldap.rb', line 1102 def initialize_ldap_con Net::LDAP.new( self.settings[:connection] ) end |
.inspect ⇒ Object
AR returns a string like “User id:integer name:string mail:text
” multi-valued attributes will be text
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/passiveldap.rb', line 402 def inspect() a = column_names b = self.name a.each { |e| if e == "id" then b = b + " id:integer" else if attrs[attr_mapfrom[e.to_sym]][:multi_valued] then b = b + " #{e}:text" else b = b + " #{e}:string" end end } b end |
.passive_ldap(connection_attributes) ⇒ Object
sets the connection and record attributes that are used. The parameter is a hash with the following options. If there are parameters missing, then the default values will be used instead of them.
-
:connection
: The:connection
is a hash that will be passed without modification to Net::LDAP. The default value is to connect to localhost on port 389 as anonymous. -
:id_attribute
: The:id_attribute
is a symbol, that tells PassiveLDAP which attribute is used as the id of a record. This attribute must be an integer attribute and it must be unique. (Although there are no constraint checkings yet) -
:multiple_record_filter
: The:multiple_record_filter
is a Proc object with one argument, that should return a Net::LDAP::Filter object that will return all the appropriate records in the directory. The default value is a filter that filters out the object based whether their attribute that is sat in:id_attribute
is set. The first argument of the block will be set to the caller PassiveLDAP object. -
:single_record_filter
: The:single_record_filter
is a Proc object with two arguments: the caller PassiveLDAP object and an id number. The corresponding block should return a filter that will filter out the record which has the appropriate id. The default value of this argument is to check whether the attribute set with:id_attribute
is equal to the specified id number. -
:record_base
: The:record_base
is a String that is set to the base of the records. The default value is “ou=users,dc=com” -
:record_scope
: The:record_scope
is a Net::LDAP::Scope object that sets the scope of the records according to the:record_base.
The default value is Net::LDAP::SearchScope_SingleLevel -
:new_id
: The:new_id
is a Proc object that will return an integer which should be an id that is not present in the directory. The default value is 10000 + count*5 + rand(5) which is not really safe -
:default_array_separator
: sets the string that will separate the multi-valued attributes if they are converted to string. Set to nil if you don’t want this conversion. This separator may be set with array_separator in an instance too. If this attribute is not nil every attribute setter/getter excluding get_attribute and set_attribute will use a converted string to set/get these attributes. If the separator is n then trailing r characters will be chomped from the splitted strings. -
:default_protection_level
: sets the default level. All attributes added after this is set wil have this default level number, unless they explicit specify something else. Default is 0
example (as well as the default values):
passive_ldap :connection => {:host => "127.0.0.1", :port => "389", :auth => { :method => :anonymous } },
:id_attribute => :id,
:multiple_record_filter => Proc.new { |s| Net::LDAP::Filter.eq(s.settings[:id_attribute].id2name,"*") },
:single_record_filter => Proc.new { |s,id| Net::LDAP::Filter.eq(s.settings[:id_attribute].id2name,id) },
:record_base => "ou=users,dc=com",
:record_scope => Net::LDAP::SearchScope_SingleLevel,
:new_id => Proc.new { |s| 10000 + s.class.count*5 + rand(5) },
:default_array_separator => nil,
:default_protection_level => 0
1015 1016 1017 |
# File 'lib/passiveldap.rb', line 1015 def passive_ldap(connection_attributes) write_inheritable_hash(:connection, connection_attributes) end |
.passive_ldap_attr(attribs) ⇒ Object
Sets the attributes you would like to use. Only the attributes set here, the attribute of the id and the dn attribute will be queried from the directory. The id_attribute and dn attributes are used automatically so they must not be set here (unless you define the dn attribute hidden with a default_value). The id attribute is always mapped to the name :id
regardless of it’s original name.
All attributes will get a getter and a setter method with their respective name (unless a mapping is defined in attribute_map), as well as a query method, that queries whether the attribute is set or not. They also get an instance variable with their mapped name (although it is only used to write to. Some AR specific methods may read the attributes data from instance variables. PassiveLDAP stores the attributes in the @attributes Hash)
By default there are no attributes defined. Multiple calls of this method will result in the union of the attributes
The attributes are set as a Hash, where the key is the name of the attribute and the value is a Hash with the following options:
-
:type
: defines a Hash with a:from
, a:to
and a:klass
attribute, from wchich the:klass
attribute must be “String”. Internally all data’s are stored as Strings (or array-of-strings if multi-valued).:from
describes a Proc that will convert the internally represented String to the class defined in:klass
(which is currently a String), and:to
will define the inverse of this conversion. The whole:type
attribute may be nil, which means there are no conversions, and the attribute is a String (or an Array of Strings). The default value is that the:from
and:to
attributes are Proc objects that will return their parameter back. The:klass
is always String, and can not be changed. This type conversion will be done with all attribute changing methods, except #get_attribute, #set_attribute. Besides the value of the:default_value
parameter won’t be converted either. Array_separator conversions are done before using this conversion. Some types are defined as constants in PassiveLDAP::Types -
:multi_valued
: tells whether the attribute can be multi_valued or not. multi_valued attributes will be arrays of string -
:level
: sets the protection level that is needed to update this attribute. Check set_protection_level for details. Default is 0 -
:name
: sets the name/mapping of the attribute. By default it is the same as the attribute’s name. When accessing the attribute (using methods, [], get_variable, etc.) you have to reference it by it’s new name. Internally the attributes will be stored with their original attribute name. -
:default_value
: the default value of the attribute, if the value of the attribute is empty when saving. Must be a String/Array or a Proc object, that will return a String or an Array. The parameter of the proc object will be the PassiveLDAP object itself. If nil there is no default value. Default is nil -
:hidden
: if true, the object will be loaded from the directory, but it’s not accessable using methods, [], and such, and will be hidden from the columns too. The @attributes instance variable will still hold it’s value, and it will be saved back to the directory when changed. Useful for attributes likeobjectclass
. Default is false. -
:always_update
: if true, and there is a default value given, before save the attribute will always get it’s default value regardles of it’s original value. Useful for timestamp or aggregate type attributes. Default is false. -
:read_only
: sets the attribute to be read only. If a default value is given saving will update this attribute too if it is empty. This is useful if the attribute needs a default value at creation but should be read-only otherwise. Default is false.
TODO: more types
TODO: name conflict checking for the mapped names
Attributes must be lowercase symbols, because Net::LDAP treats them that way!
example:
passive_ldap_attr :name => {}, :sn => {}, :cn => {}
passive_ldap_attr :name => {:level => 1}, :sn => {:level => 1}, :cn => {:level => 1}
passive_ldap_attr :mail => {:multi_valued => true, :level => 1}, :mobile => {:multi_valued => true, :level => 1}
passive_ldap_attr :roomnumber => {:level => 2}
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 |
# File 'lib/passiveldap.rb', line 1067 def passive_ldap_attr(attribs) mapto = {} mapfrom = {} nohidden = {} attribs.each { |key, value| value[:multi_valued] ||= false value[:level] ||= self.settings[:default_protection_level] value[:type] ||= nil if (value[:type]) then value[:type][:from] ||= Proc.new { |s| s } value[:type][:to] ||= Proc.new { |s| s } value[:type][:klass] = String end value[:name] ||= key value[:default_value] ||= nil value[:hidden] ||= false value[:always_update] ||= false value[:read_only] ||= false value[:read_only] = value[:read_only] or value[:hidden] raise DistinguishedNameException, "DN attribute can't have the always_update flag set" if key == :dn and value[:always_update] raise DistinguishedNameException, "DN attribute must be hidden" if key == :dn and !value[:hidden] raise DistinguishedNameException, "DN attribute must have a default_value" if key == :dn and value[:default_value].nil? unless value[:hidden] mapto[key] = value[:name] mapfrom[value[:name]] = key nohidden[key] = value end } write_inheritable_hash(:attr_orig, attribs) write_inheritable_hash(:attrs, nohidden) write_inheritable_hash(:mapto, mapto) write_inheritable_hash(:mapfrom, mapfrom) end |
.primary_key ⇒ Object
AR returns :id
420 421 422 |
# File 'lib/passiveldap.rb', line 420 def primary_key :id end |
.serialize(attr_name, class_name = Object) ⇒ Object
AR not implemented. Will raise ARMethodMissing
425 426 427 |
# File 'lib/passiveldap.rb', line 425 def serialize(attr_name, class_name = Object) raise ARMethodMissing, "ARMethodMissing: serialize" end |
.serialized_attributes ⇒ Object
AR not implemented. Will raise ARMethodMissing
430 431 432 |
# File 'lib/passiveldap.rb', line 430 def serialized_attributes raise ARMethodMissing, "ARMethodMissing: serialized_attributes" end |
.settings ⇒ Object
gets the hash set with #passive_ldap
154 155 156 |
# File 'lib/passiveldap.rb', line 154 def settings read_inheritable_attribute(:connection) end |
.table_name ⇒ Object
AR will return the name of the class
435 436 437 |
# File 'lib/passiveldap.rb', line 435 def table_name self.name end |
.update(id, attributes) ⇒ Object
AR Updates an object or objects (if passed an Array) with the attributes given. Uses save!
440 441 442 443 444 445 446 447 448 449 450 451 452 453 |
# File 'lib/passiveldap.rb', line 440 def update(id, attributes) id = [id] unless id.kind_of?(Array) attributes = [attributes] unless attributes.kind_of?(Array) if id.length != attributes.length then raise PassiveLDAPError, "Argument numbers don't mach" end c = [] id.each_index { |v| a = new(id[v]) a.update_attributes(attributes[v]) c << a } id.length==1 ? c[0] : c end |
.update_all(updates, conditions = nil, options = {}) ⇒ Object
AR not implemented. Will raise ARMethodMissing
456 457 458 |
# File 'lib/passiveldap.rb', line 456 def update_all(updates, conditions = nil, = {}) raise ARMethodMissing, "ARMethodMissing: update_all" end |
.update_counters(id, counters) ⇒ Object
AR not implemented. Will raise ARMethodMissing
461 462 463 |
# File 'lib/passiveldap.rb', line 461 def update_counters(id,counters) raise ARMethodMissing, "ARMethodMissing: update_counters" end |
.validates_format_of_each(*attr_names) ⇒ Object
validates the format of each value in a multi-valued attribute. See ActiveRecord::Validations#validates_format_of. Only use this with multi-valued attributes!
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 |
# File 'lib/passiveldap.rb', line 1108 def validates_format_of_each(*attr_names) configuration = { :message => ActiveRecord::Errors.[:invalid], :on => :save, :with => nil } configuration.update(attr_names.) raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp) validates_each(attr_names, configuration) do |record, attr_name, value| if value.nil? then record.errors.add(attr_name, configuration[:message]) else if settings[:default_array_separator].nil? then value.each { |val| record.errors.add(attr_name, configuration[:message]) unless val.to_s =~ configuration[:with] } else value.split(settings[:default_array_separator]).each { |val| val.chomp!("\r") if settings[:default_array_separator] == "\n" record.errors.add(attr_name, configuration[:message]) unless val.to_s =~ configuration[:with] } end end end end |
Instance Method Details
#[](attribute) ⇒ Object
AR gets the value of the attribute. If the attribute has an alternate name then you have to use it here
725 726 727 |
# File 'lib/passiveldap.rb', line 725 def [](attribute) read_mapped_attribute(attribute) end |
#[]=(attribute, value) ⇒ Object
AR sets the value of the attribute. If the attribute has an alternate name then you have to use it here
730 731 732 |
# File 'lib/passiveldap.rb', line 730 def []=(attribute,value) write_mapped_attribute(attribute,value) end |
#array_separator(new_sep = nil) ⇒ Object
sets the array_separator
653 654 655 |
# File 'lib/passiveldap.rb', line 653 def array_separator(new_sep = nil) @array_separator = new_sep end |
#attribute_names ⇒ Object
AR Returns an array of symbols of the attributes that can be changed; sorted alphabetically
735 736 737 738 |
# File 'lib/passiveldap.rb', line 735 def attribute_names() a = self.class.column_names a.collect { |e| e.to_sym }.sort end |
#attribute_present?(attribute) ⇒ Boolean
AR Returns true if the specified attribute has been set by the user or by a database load and is neither nil nor empty?
It will always be true for the :id
and :dn
attribute (even if the :dn
is not set)
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 |
# File 'lib/passiveldap.rb', line 744 def attribute_present?(attribute) attribute = attribute.to_sym unless attribute.kind_of?(Symbol) return true if attribute == :id or attribute == :dn return false unless attribute_names.include?(attribute) a = self.class.attr_mapfrom[attribute] if @attributes[a].nil? then false elsif @attributes[a].kind_of?(Array) then if @attributes[a] == [] then false else true end elsif @attributes[a].kind_of?(String) then if @attributes[a] == "" then false else true end else true end end |
#attributes(options = nil) ⇒ Object
AR Returns a hash of all the attributes with their names as keys and clones of their objects as values.
Options will be ignored (is it used in AR anyway?)
771 772 773 774 775 776 777 778 779 780 781 |
# File 'lib/passiveldap.rb', line 771 def attributes( = nil) a = { :id => id } @attributes.each { |key,value| v = value v = value.clone if value.duplicable? if self.class.attrs.has_key?(key) then a[self.class.attrs[key][:name]] = v end } a end |
#attributes=(new_attributes, guard_protected_attribute = true) ⇒ Object
AR sets multiple attributes at once. if guard_protected_attributes if true only level settings[:default_protection_level]
attributes will be changed. guard_protected_attributes may be set to an Integer, indicating which is the maximum level of the attributes that need to be changed, or to false indicating that all attributes need to be changed
786 787 788 789 790 791 792 793 794 795 796 797 798 |
# File 'lib/passiveldap.rb', line 786 def attributes=(new_attributes, guard_protected_attribute = true) guard_protected_attribute = self.class.settings[:default_protection_level] if guard_protected_attribute == true new_attributes.each { |key,value| k = key k = key.to_sym unless key.kind_of?(Symbol) if self.class.attr_mapfrom.has_key?(k) then level = self.class.attrs[self.class.attr_mapfrom[k]][:level] if !guard_protected_attribute or (guard_protected_attribute.kind_of?(Integer) and guard_protected_attribute >= level) then self[k] = value end end } end |
#bind(password = nil, username = nil) ⇒ Object
Bind to the directory to check whether the credentials are right or not. If there are no parameters specified bind will do the following:
-
If the actual protection_level is 0 it will bind with the default connection
-
If the level is 1 it will bind with the dn of the record and the password, that is set with #set_protection_level
-
If the level is above 2 it will bind with the dn and password set with #set_protection_level
Parameters may be used to set the dn and the password used to bind to the directory. Beware! The first parameter is the password! You may omit the username, in which case the dn of the record will be used to bind to the directory
bind will return true if the connection is succesful and will raise a ConnectionError with a message from the server if the authentication fails
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 |
# File 'lib/passiveldap.rb', line 485 def bind(password = nil, username = nil) if password then ldap = self.class.initialize_ldap_con if username then ldap.authenticate(username,password) else ldap.authenticate(dn,password) end ldap.bind raise ConnectionError, ldap.get_operation_result. unless ldap.get_operation_result.code == 0 else ldap = initialize_ldap_con ldap.bind raise ConnectionError, ldap.get_operation_result. unless ldap.get_operation_result.code == 0 end true end |
#clone ⇒ Object
AR not implemented. Raises ARMethodMissing
801 802 803 |
# File 'lib/passiveldap.rb', line 801 def clone raise ARMethodMissing, "ARMethodMissing: clone" end |
#column_for_attribute(name) ⇒ Object
AR returns the column object of the named attribute
806 807 808 |
# File 'lib/passiveldap.rb', line 806 def column_for_attribute(name) self.class.columns_hash[name.to_s] end |
#destroy ⇒ Object
AR deletes the record in the directory and freezes the object
811 812 813 814 815 816 |
# File 'lib/passiveldap.rb', line 811 def destroy ldap = initialize_ldap_con ldap.delete(:dn => dn) raise ConnectionError, ldap.get_operation_result. unless ldap.get_operation_result.code == 0 freeze end |
#dn ⇒ Object
gets the distinguished name of the record. Returns nil if the record is nonexistent in the directory
565 566 567 |
# File 'lib/passiveldap.rb', line 565 def dn @attributes[:dn] end |
#dn=(newdn) ⇒ Object
sets the distinguished name of the record. The dn can only be set when the record is not originated from the directory (so it is a new record) Otherwise a DistinguishedNameException is raised
571 572 573 574 575 |
# File 'lib/passiveldap.rb', line 571 def dn=(newdn) raise PassiveLDAP::DistinguishedNameException, "DN cannot be changed" unless @oldattr[:dn].nil? @dn = newdn @attributes[:dn]=newdn end |
#exists_in_directory ⇒ Object
returns whether the record is new, or it is originated from the directory
if it exists it will return the dn of the record, if not it will return nil
580 581 582 |
# File 'lib/passiveldap.rb', line 580 def exists_in_directory @oldattr[:dn] end |
#get_attribute(attribute) ⇒ Object
returns the attrbiute. If it is multi_valued no conversion will be done even if the array_separator is something else than nil
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 |
# File 'lib/passiveldap.rb', line 605 def get_attribute(attribute) attribute = attribute.to_sym unless attribute.kind_of?(Symbol) if self.class.attr_mapfrom.has_key?(attribute) then key = self.class.attr_mapfrom[attribute] if @attributes.has_key?(key) then @attributes[key] else nil end else if attribute == :id then self.id else raise PassiveLDAP::AttributeAssignmentError, "Attribute #{attribute} does not exist" end end end |
#get_old_attribute(attribute) ⇒ Object
gets the original value (the value that was read from the directory, or nil if this is a new record) of an attribute
585 586 587 588 589 590 591 592 593 594 595 596 |
# File 'lib/passiveldap.rb', line 585 def get_old_attribute(attribute) attribute = attribute.to_sym unless attribute.kind_of?(Symbol) if self.class.attr_mapfrom.has_key?(attribute) then @oldattr[self.class.attr_mapfrom[attribute]] else if attribute == :id then @oldattr[self.settings[:id_attribute]] else raise PassiveLDAP::AttributeAssignmentError, "Attribute #{attribute} does not exist" end end end |
#id ⇒ Object
AR gets the id of the record
819 820 821 |
# File 'lib/passiveldap.rb', line 819 def id @attributes[self.class.settings[:id_attribute]] end |
#id=(a) ⇒ Object
AR sets the id of the record
824 825 826 827 828 |
# File 'lib/passiveldap.rb', line 824 def id=(a) raise PassiveLDAP::AttributeAssignmentError, "Id must be an integer" unless a.kind_of?(Integer) or (a.kind_of?(String) and a.to_i.to_s == a) @attributes[self.class.settings[:id_attribute]] = a @id = a end |
#id? ⇒ Boolean
gets whether the id is set. Returns always true
560 561 562 |
# File 'lib/passiveldap.rb', line 560 def id? true end |
#inspect ⇒ Object
AR Returns the contents of the record as a string
should be nicer
833 834 835 |
# File 'lib/passiveldap.rb', line 833 def inspect "#{self.class.name}: #{attributes.inspect}" end |
#new_record? ⇒ Boolean
AR Returns true if this object hasn’t been saved yet - that is, a record for the object doesn’t exist in the directory yet.
838 839 840 841 842 843 844 |
# File 'lib/passiveldap.rb', line 838 def new_record? if exists_in_directory then false else true end end |
#reload(options = nil) ⇒ Object
AR reloads the data from the directory. If the record does not exists it will erase all attributes and set id to the old value. If the record was acquired from the directory and the id was changed the old id will be used to load the data, but the id will be set to the new one after the data has benn loaded. This may be changed with the :newid
option
options may be
-
:id
: set the id to this new value. If set the:newid
attribute won’t be checked -
:oldattr
: set to true if you want to load the attributes only into the @oldattr variable, but not into the @attributes -
:newid
: set to true if you want to load the new id’s data (if you changed the id of the data before reloading)
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 |
# File 'lib/passiveldap.rb', line 854 def reload( = nil) = {} if .nil? id_set = true [:newid] ||= false [:oldattr] ||= false unless .has_key?(:id) then id_set = false new_id = id [:id] ||= id [:id] = @oldattr[self.class.settings[:id_attribute]] unless [:newid] end @oldattr = {} ldap = self.class.initialize_ldap_con entry = ldap.search( :base => self.class.settings[:record_base], :scope => self.class.settings[:record_scope], :filter => self.class.settings[:single_record_filter].call(self.class,[:id].to_s) ) raise ConnectionError, ldap.get_operation_result. unless ldap.get_operation_result.code == 0 if entry and entry != [] then @oldattr[:dn] = entry[0].dn.downcase entry[0].each { |name, values| if self.class.attrs_all.has_key?(name) then if self.class.attrs_all[name][:multi_valued] then @oldattr[name] = values else @oldattr[name] = values[0] end end } else @oldattr[:dn] = nil end @oldattr[self.class.settings[:id_attribute]] = [:id] unless [:oldattr] then @attributes = @oldattr.clone @dn = @attributes[:dn] @attributes.each { |key,value| if self.class.attrs.has_key?(key) then alt_name = self.class.attrs[key][:name] eval "@#{alt_name.to_s} = value" end } @id = [:id] if !id_set and ![:newid] then @attributes[self.class.settings[:id_attribute]] = new_id @id = new_id end end end |
#respond_to_without_attributes?(method, include_priv = false) ⇒ Boolean
AR needed by ActiveRecord::Callbacks
902 903 904 905 906 907 908 |
# File 'lib/passiveldap.rb', line 902 def respond_to_without_attributes?(method, include_priv=false) method_name = method.to_s method_name.chomp!("?") method_name.chomp!("!") return false if self.class.attr_mapfrom.has_key?(method_name.to_sym) respond_to?(method, include_priv) end |
#save ⇒ Object
AR Saves the changes back to the LDAP server. Only the changes will be saved, and only those attributes will be saved whose protection level is less or equal than the actual protection level.
Attributes with default values will get their new values calculated
The modifications will be sent to server as one modification chunk, but it depends on the LDAP server whether it will modify the directory as an atomic transaction. If an error occurs you should check whether the directory remained in a consistent state. See Net::LDAP#modify for more information
Before saving the attributes are loaded from the server to check what has changed. Between the loading and the saving other threads may modify the directory so be aware of this.
TODO: some kind of locking system
Returns false if an error occurs.
930 931 932 933 934 935 936 937 938 |
# File 'lib/passiveldap.rb', line 930 def save save! rescue RecordNotSaved => e return false rescue ActiveRecord::RecordInvalid return false else return true end |
#save! ⇒ Object
AR saves the record but will raise a RecordNotSaved with the cause of the failure if unsuccesful. See save
941 942 943 944 945 946 |
# File 'lib/passiveldap.rb', line 941 def save! create_or_update rescue RecordNotSaved => e @errors.add_to_base(e) raise end |
#set_attribute(attribute, value, raise_error_when_readonly = false) ⇒ Object
sets the attribute. If it is multi_valued you need to pass an array even if the array_separator is set
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
# File 'lib/passiveldap.rb', line 625 def set_attribute(attribute,value, raise_error_when_readonly = false) attribute = attribute.to_sym unless attribute.kind_of?(Symbol) if self.class.attr_mapfrom.has_key?(attribute) then alt_name = self.class.attr_mapfrom[attribute] if self.class.attrs[alt_name][:read_only] if raise_error_when_readonly then raise PassiveLDAP::AttributeAssignmentError, "Attribute #{attribute} is read-only" else return false end end if self.class.attrs[alt_name][:multi_valued] then raise PassiveLDAP::AttributeAssignmentError, "Array expected, because #{attribute} is multi-valued" unless value.kind_of?(Array) else raise PassiveLDAP::AttributeAssignmentError, "Didn't expect an Array, because #{attribute} is not multi-valued" if value.kind_of?(Array) end eval "@#{attribute.to_s} = value" @attributes[alt_name] = value else if attribute == :id then self.id=value else raise PassiveLDAP::AttributeAssignmentError, "Attribute #{attribute} does not exist" end end end |
#set_password(newpass, method, options = nil) ⇒ Object
changes the password of the record.
Currently method may only be :active_directory
For options check #set_password_ad
will return false if unsuccesful, adding the response from the server to the errors list
510 511 512 513 514 515 516 |
# File 'lib/passiveldap.rb', line 510 def set_password(newpass, method, = nil) set_password! rescue RecordNotSaved return false else return true end |
#set_password!(newpass, method, options = nil) ⇒ Object
same as set_password but will raise a RecordNotSaved exception in unsuccesful
519 520 521 522 523 524 525 526 527 528 |
# File 'lib/passiveldap.rb', line 519 def set_password!(newpass, method, = nil) if method == :active_directory then set_password_ad(newpass, ) else raise ARFeatureMissing, "Only AD password changes supported!" end rescue Exception => e @errors.add_to_base(e) raise end |
#set_protection_level(level = 0, password = nil, username = nil) ⇒ Object
Attributes may have different protection levels. Protection level means, that some attributes may only be changed by privileged users. Level 0 means that the attribute may be changed by the main connection. Level 1 means, the attribute can be changed by the owner of the attribute, but cannot be changed by the main connection. Level 2 and higher level means that the attribute can only be changed with a user, who has enough privileges.
For example if PassiveLDAP is used for storing User information, you might set most of the attributes to level 1 (so the password of the user will be needed to change those information) and some attributes (such as printAccount, or like) may be set to level 2 or higher, so only privileged users (like administrators) could change those attributes.
the method has 3 paramteres. The first one sets the desired level, the second one is the password of the user (if the level is greater or equal than 1) and the third one is the username (full dn!) of the user (if the level is above 1)
Protection means that when issuing a save method, only those attributes will be saved, that are below or equal to the protection level set here, the other ones won’t be sent to the LDAP server. Of course you should set the appropriate rights in the server too for maximum security.
Class methods (like find) will be run with the connection’s authenticity information while instance methods will run with the actual username and password set with set_protection_level
Beware! the second parameter is the password and the third is the username!
553 554 555 556 557 |
# File 'lib/passiveldap.rb', line 553 def set_protection_level(level = 0, password = nil, username = nil) @protection_level = level @protection_username = username @protection_password = password end |
#to_s ⇒ Object
returns the user id as string
599 600 601 |
# File 'lib/passiveldap.rb', line 599 def to_s @id.to_s end |
#update_attribute(name, value) ⇒ Object
AR updates a single attribute and saves the record. See ActiveRecord::Base#update_attribute
949 950 951 952 |
# File 'lib/passiveldap.rb', line 949 def update_attribute(name, value) self[name] = value save end |
#update_attributes(attributes) ⇒ Object
AR updates multiple attributes and saves the record. See update_attribute.
955 956 957 958 959 960 961 |
# File 'lib/passiveldap.rb', line 955 def update_attributes(attributes) update_attributes!(attributes) rescue RecordNotFound return false else return true end |
#update_attributes!(attributes) ⇒ Object
AR see update_attributes. Uses save! instead of save
964 965 966 967 |
# File 'lib/passiveldap.rb', line 964 def update_attributes!(attributes) self.attributes=(attributes) save! end |