root/lib/ldap_shadows/elements/object.rb @ 4b490148
89d8bebc | Marc Dequènes (Duck) | #--
|
|
# LdapShadows, a Medium-level LDAP Access Library and Tool.
|
|||
bc2c2691 | Marc Dequènes (Duck) | # Copyright (c) 2009-2010 Marc Dequènes (Duck) <Duck@DuckCorp.org>
|
|
89d8bebc | Marc Dequènes (Duck) | #
|
|
# This program is free software: you can redistribute it and/or modify
|
|||
# it under the terms of the GNU General Public License as published by
|
|||
# the Free Software Foundation, either version 3 of the License, or
|
|||
# (at your option) any later version.
|
|||
#
|
|||
# This program is distributed in the hope that it will be useful,
|
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
# GNU General Public License for more details.
|
|||
#
|
|||
# You should have received a copy of the GNU General Public License
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#++
|
|||
020c18fe | Marc Dequènes (Duck) | require 'ldap_shadows/manipulation_helper'
|
|
89d8bebc | Marc Dequènes (Duck) | module LdapShadows
|
|
5a48a521 | Marc Dequènes (Duck) | module Elements
|
|
89d8bebc | Marc Dequènes (Duck) | class LdapObject < ActiveLdap::Base
|
|
90809ae4 | Marc Dequènes (Duck) | include LdapElement
|
|
88b0bbce | Marc Dequènes (Duck) | ||
020c18fe | Marc Dequènes (Duck) | FULL_HANDLE_PATTERN = /^(?:root|([a-zA-Z_]+)\/(.+))$/
|
|
f43a4cb1 | Marc Dequènes (Duck) | ||
fa116a1b | Marc Dequènes (Duck) | attr_reader :parent_changed
|
|
90809ae4 | Marc Dequènes (Duck) | @relations_info = {}
|
|
a8bda213 | Marc Dequènes (Duck) | @virtual_attributes = {}
|
|
89d8bebc | Marc Dequènes (Duck) | ||
90809ae4 | Marc Dequènes (Duck) | class << self
|
|
0032f05d | Marc Dequènes (Duck) | attr_reader :relations_info
|
|
90809ae4 | Marc Dequènes (Duck) | end
|
|
# default
|
|||
89d8bebc | Marc Dequènes (Duck) | ldap_mapping :prefix => '', :classes => ['top'], :scope => :sub
|
|
681c944d | Marc Dequènes (Duck) | # always called for instanciated objects
|
|
#
|
|||
# Note: ActiveLdap uses allocate to instanciate objects, then
|
|||
# call initialize only for DB-new objects, and
|
|||
# call initialize_by_ldap_data for DB-loaded objects
|
|||
def init_instance_variables
|
|||
super
|
|||
fa116a1b | Marc Dequènes (Duck) | ||
@parent_changed = false
|
|||
bf962db8 | Marc Dequènes (Duck) | @latest_parent_full_handle = nil
|
|
c2c26953 | Marc Dequènes (Duck) | @aspects_inst_cache = {}
|
|
fa116a1b | Marc Dequènes (Duck) | end
|
|
c2c26953 | Marc Dequènes (Duck) | def virtual_attributes
|
|
list = self.class.virtual_attributes
|
|||
aspects.values.each do |aspect|
|
|||
list += aspect.virtual_attributes
|
|||
end
|
|||
46d859e0 | Marc Dequènes (Duck) | list
|
|
a8bda213 | Marc Dequènes (Duck) | end
|
|
# return nil if not found
|
|||
def attribute_info(attr)
|
|||
c2c26953 | Marc Dequènes (Duck) | return self.class.real_attribute_info(attr) if self.has_attribute? attr
|
|
info = self.class.virtual_attribute_info(attr)
|
|||
return info unless info.nil?
|
|||
aspects.values.each do |aspect|
|
|||
info = aspect.virtual_attribute_info(attr)
|
|||
return info unless info.nil?
|
|||
a8bda213 | Marc Dequènes (Duck) | end
|
|
46d859e0 | Marc Dequènes (Duck) | ||
nil
|
|||
a8bda213 | Marc Dequènes (Duck) | end
|
|
38e5c5db | Marc Dequènes (Duck) | def handle
|
|
98217996 | Marc Dequènes (Duck) | name = self[dn_attribute] || self.attributes[dn_attribute] || self.dn.to_s
|
|
707b1758 | Marc Dequènes (Duck) | name = name.first if name.is_a? Array
|
|
38e5c5db | Marc Dequènes (Duck) | name.strip
|
|
end
|
|||
def full_handle
|
|||
"#{self.class.handle}/#{self.handle}"
|
|||
end
|
|||
90809ae4 | Marc Dequènes (Duck) | def self.cast
|
|
f25d0aed | Marc Dequènes (Duck) | super
|
|
3a652e30 | Marc Dequènes (Duck) | ldap_mapping self.parameters[:mapping].reject {|key, val| not ActiveLdap::Base::VALID_LDAP_MAPPING_OPTIONS.include?(key)}
|
|
90809ae4 | Marc Dequènes (Duck) | end
|
|
def self.cast_relations
|
|||
f25d0aed | Marc Dequènes (Duck) | super
|
|
90809ae4 | Marc Dequènes (Duck) | object_rel = {}
|
|
object_rel.merge!(self.parameters[:relations]) if self.parameters.include?(:relations)
|
|||
aa15cd46 | Marc Dequènes (Duck) | self.possible_aspects.each do |aspect_name|
|
|
accc97fc | Marc Dequènes (Duck) | aspect = self.shadow.get_aspect(aspect_name)
|
|
if aspect.nil?
|
|||
raise PreProcessingError, _("Aspect '%s' is missing for object '%s'") % [aspect_name, self.handle]
|
|||
90809ae4 | Marc Dequènes (Duck) | end
|
|
5b1a6342 | Marc Dequènes (Duck) | object_rel.merge!(aspect.parameters[:relations])
|
|
90809ae4 | Marc Dequènes (Duck) | end
|
|
object_relations_info = {}
|
|||
object_rel.each_pair do |field_name, rel|
|
|||
foreign_klass = self.shadow.get_object(rel[:object])
|
|||
if foreign_klass.nil?
|
|||
raise PreProcessingError, _("Relation '%s' for object '%s' is impossible: foreign object '%s' is missing") % [field_name, self.handle, rel[:object]]
|
|||
end
|
|||
rel[:class_name] = foreign_klass.to_s
|
|||
98217996 | Marc Dequènes (Duck) | rel_attr_val = rel[:foreign_key]
|
|
90809ae4 | Marc Dequènes (Duck) | case rel[:type]
|
|
when 'belongs_to'
|
|||
belongs_to field_name, rel.reject {|key, val| not ActiveLdap::Associations::ClassMethods::VALID_BELONGS_TO_OPTIONS.include?(key) }
|
|||
98217996 | Marc Dequènes (Duck) | rel_attr_val = rel[:primary_key] if rel[:many] and rel[:primary_key]
|
|
90809ae4 | Marc Dequènes (Duck) | when 'has_many'
|
|
has_many field_name, rel.reject {|key, val| not ActiveLdap::Associations::ClassMethods::VALID_HAS_MANY_OPTIONS.include?(key) }
|
|||
else
|
|||
raise "bug in '#{self.handle}' object relations (wrong type)"
|
|||
end
|
|||
object_relations_info[field_name] = {
|
|||
:foreign_klass => foreign_klass,
|
|||
98217996 | Marc Dequènes (Duck) | :single_value => ActiveLdap::Base.schema.attribute(rel_attr_val).single_value?,
|
|
90809ae4 | Marc Dequènes (Duck) | :read_only => rel[:read_only] || false
|
|
}
|
|||
end
|
|||
0032f05d | Marc Dequènes (Duck) | instance_variable_set(:@relations_info, object_relations_info)
|
|
90809ae4 | Marc Dequènes (Duck) | end
|
|
5d28ad6e | Marc Dequènes (Duck) | def has_field?(field)
|
|
return false if field.downcase == "objectclass"
|
|||
c2c26953 | Marc Dequènes (Duck) | return true if has_attribute?(field)
|
|
return true if virtual_attributes.include?(field)
|
|||
aspects.values.each do |aspect|
|
|||
return true if aspect.virtual_attributes.include?(field)
|
|||
end
|
|||
return false
|
|||
5d28ad6e | Marc Dequènes (Duck) | end
|
|
89d8bebc | Marc Dequènes (Duck) | def human_name
|
|
718238ae | Marc Dequènes (Duck) | attr_list = ['displayName', 'cn']
|
|
66e78c7a | Marc Dequènes (Duck) | name_attribute = self.class.parameters[:interface][:name_attribute]
|
|
accc97fc | Marc Dequènes (Duck) | attr_list.unshift(name_attribute) unless name_attribute.nil?
|
|
718238ae | Marc Dequènes (Duck) | attr_list.each do |attr|
|
|
if attr == 'dn'
|
|||
98217996 | Marc Dequènes (Duck) | return self.dn.to_s
|
|
718238ae | Marc Dequènes (Duck) | elsif self.attribute_present?(attr)
|
|
val = self.send(attr, true)
|
|||
2af0173c | Marc Dequènes (Duck) | return val[0].to_s.strip
|
|
89d8bebc | Marc Dequènes (Duck) | end
|
|
end
|
|||
return ""
|
|||
end
|
|||
378df927 | Marc Dequènes (Duck) | def human_description
|
|
718238ae | Marc Dequènes (Duck) | attr_list = ['description']
|
|
66e78c7a | Marc Dequènes (Duck) | desc_attribute = self.class.parameters[:interface][:desc_attribute]
|
|
accc97fc | Marc Dequènes (Duck) | attr_list.unshift(desc_attribute) unless desc_attribute.nil?
|
|
718238ae | Marc Dequènes (Duck) | attr_list.each do |attr|
|
|
if self.attribute_present?(attr)
|
|||
89d8bebc | Marc Dequènes (Duck) | return self[attr].is_a?(Array) ? self[attr][0] : self[attr]
|
|
end
|
|||
end
|
|||
return ""
|
|||
end
|
|||
def possible_relations
|
|||
self.associations.collect {|assoc| assoc.to_s } - ['children']
|
|||
end
|
|||
def relations
|
|||
428fdd46 | Marc Dequènes (Duck) | rel_list = self.class.parameters[:mapping][:associated_relations]
|
|
89d8bebc | Marc Dequènes (Duck) | ||
5b1a6342 | Marc Dequènes (Duck) | aspects.values.each do |aspect|
|
|
428fdd46 | Marc Dequènes (Duck) | rel_list += aspect.parameters[:mapping][:associated_relations]
|
|
89d8bebc | Marc Dequènes (Duck) | end
|
|
rel_list & possible_relations
|
|||
end
|
|||
6089b33a | Marc Dequènes (Duck) | def self.possible_aspects
|
|
428fdd46 | Marc Dequènes (Duck) | self.parameters[:mapping][:possible_aspects].sort
|
|
6089b33a | Marc Dequènes (Duck) | end
|
|
89d8bebc | Marc Dequènes (Duck) | def aspects
|
|
5b1a6342 | Marc Dequènes (Duck) | present_aspects = {}
|
|
aa15cd46 | Marc Dequènes (Duck) | self.class.possible_aspects.each do |aspect_name|
|
|
5b1a6342 | Marc Dequènes (Duck) | aspect = self.class.shadow.get_aspect(aspect_name)
|
|
aspect_mapping = aspect.parameters[:mapping]
|
|||
present_aspects[aspect.handle] = aspect if self.classes & aspect_mapping[:classes] == aspect_mapping[:classes]
|
|||
89d8bebc | Marc Dequènes (Duck) | end
|
|
present_aspects
|
|||
end
|
|||
f7217dcd | Marc Dequènes (Duck) | def organized_data
|
|
66e78c7a | Marc Dequènes (Duck) | attr_list = self.nonempty_attributes
|
|
89d8bebc | Marc Dequènes (Duck) | rel_list = self.possible_relations
|
|
# first pass to take aspects forced relations into account
|
|||
obj_aspects = {}
|
|||
5b1a6342 | Marc Dequènes (Duck) | self.aspects.values.each do |aspect|
|
|
66e78c7a | Marc Dequènes (Duck) | aspect_mapping = aspect.parameters[:mapping]
|
|
00fff28a | Marc Dequènes (Duck) | obj_aspects[aspect.handle] ||= {}
|
|
89d8bebc | Marc Dequènes (Duck) | ||
66e78c7a | Marc Dequènes (Duck) | unless aspect_mapping[:associated_attributes].empty?
|
|
taken_attr_list = aspect_mapping[:associated_attributes] & attr_list
|
|||
89d8bebc | Marc Dequènes (Duck) | unless taken_attr_list.empty?
|
|
00fff28a | Marc Dequènes (Duck) | taken_attr_list.each do |attr|
|
|
data = fetch_attribute_data(attr, aspect)
|
|||
obj_aspects[aspect.handle][attr] = data unless data.nil?
|
|||
end
|
|||
89d8bebc | Marc Dequènes (Duck) | attr_list -= taken_attr_list
|
|
end
|
|||
end
|
|||
66e78c7a | Marc Dequènes (Duck) | unless aspect_mapping[:associated_relations].empty?
|
|
taken_rel_list = aspect_mapping[:associated_relations] & rel_list
|
|||
89d8bebc | Marc Dequènes (Duck) | unless taken_rel_list.empty?
|
|
00fff28a | Marc Dequènes (Duck) | taken_rel_list.each do |rel|
|
|
data = fetch_relation_data(rel, aspect)
|
|||
obj_aspects[aspect.handle]['rel:' + rel] = data unless data.nil?
|
|||
end
|
|||
89d8bebc | Marc Dequènes (Duck) | rel_list -= taken_rel_list
|
|
end
|
|||
end
|
|||
end
|
|||
# manage general attributes
|
|||
obj_info = {}
|
|||
428fdd46 | Marc Dequènes (Duck) | if self.class.parameters[:mapping][:associate_unclaimed_attributes]
|
|
89d8bebc | Marc Dequènes (Duck) | taken_attr_list = attr_list
|
|
else
|
|||
66e78c7a | Marc Dequènes (Duck) | taken_attr_list = self.class.parameters[:mapping][:associated_attributes]
|
|
a24bc5b1 | Marc Dequènes (Duck) | taken_attr_list += self.class.possible_attributes
|
|
89d8bebc | Marc Dequènes (Duck) | end
|
|
c3bd9816 | Marc Dequènes (Duck) | taken_attr_list -= self.class.parameters[:mapping][:ignored_attributes]
|
|
89d8bebc | Marc Dequènes (Duck) | taken_attr_list = taken_attr_list.uniq & attr_list
|
|
a8bda213 | Marc Dequènes (Duck) | taken_attr_list += self.class.virtual_attributes
|
|
00fff28a | Marc Dequènes (Duck) | taken_attr_list.each do |attr|
|
|
data = fetch_attribute_data(attr, self)
|
|||
obj_info[attr] = data unless data.nil?
|
|||
end
|
|||
89d8bebc | Marc Dequènes (Duck) | attr_list -= taken_attr_list
|
|
# manage general relations
|
|||
428fdd46 | Marc Dequènes (Duck) | if self.class.parameters[:mapping][:associated_relations]
|
|
taken_rel_list = self.class.parameters[:mapping][:associated_relations] & rel_list
|
|||
89d8bebc | Marc Dequènes (Duck) | unless taken_rel_list.empty?
|
|
00fff28a | Marc Dequènes (Duck) | taken_rel_list.each do |rel|
|
|
data = fetch_relation_data(rel, self)
|
|||
obj_info['rel:' + rel] = data unless data.nil?
|
|||
end
|
|||
89d8bebc | Marc Dequènes (Duck) | rel_list -= taken_rel_list
|
|
end
|
|||
end
|
|||
# second pass to dispath the remaining attributes
|
|||
unless attr_list.empty?
|
|||
5b1a6342 | Marc Dequènes (Duck) | self.aspects.values.each do |aspect|
|
|
taken_attr_list = (aspect.possible_attributes & attr_list)
|
|||
c3bd9816 | Marc Dequènes (Duck) | taken_attr_list -= aspect.parameters[:mapping][:ignored_attributes]
|
|
c2c26953 | Marc Dequènes (Duck) | taken_attr_list += aspect.virtual_attributes
|
|
00fff28a | Marc Dequènes (Duck) | unless taken_attr_list.empty?
|
|
taken_attr_list.each do |attr|
|
|||
data = fetch_attribute_data(attr, aspect)
|
|||
obj_aspects[aspect.handle][attr] = data unless data.nil?
|
|||
end
|
|||
attr_list -= taken_attr_list
|
|||
end
|
|||
89d8bebc | Marc Dequènes (Duck) | ||
break if attr_list.empty?
|
|||
end
|
|||
end
|
|||
66e78c7a | Marc Dequènes (Duck) | # third pass to dispath the remaining attributes
|
|
unless attr_list.empty?
|
|||
00fff28a | Marc Dequènes (Duck) | attr_list.each do |attr|
|
|
data = fetch_attribute_data(attr, self)
|
|||
obj_info[attr] = data unless data.nil?
|
|||
end
|
|||
66e78c7a | Marc Dequènes (Duck) | end
|
|
89d8bebc | Marc Dequènes (Duck) | [obj_info, obj_aspects]
|
|
end
|
|||
2bd92292 | Marc Dequènes (Duck) | def modify(key, op, val)
|
|
3d5fa735 | Marc Dequènes (Duck) | if key.index(":")
|
|
type, field = key.split(":")
|
|||
else
|
|||
type = nil
|
|||
field = key
|
|||
end
|
|||
case type
|
|||
when nil
|
|||
2bd92292 | Marc Dequènes (Duck) | modify_field(key, op, val)
|
|
3d5fa735 | Marc Dequènes (Duck) | when 'rel'
|
|
2bd92292 | Marc Dequènes (Duck) | modify_relation(field, op, val)
|
|
3d5fa735 | Marc Dequènes (Duck) | when ''
|
|
case field
|
|||
when 'aspects'
|
|||
2bd92292 | Marc Dequènes (Duck) | modify_aspects(op, val)
|
|
7414a658 | Marc Dequènes (Duck) | when 'parent'
|
|
modify_parent(op, val)
|
|||
3d5fa735 | Marc Dequènes (Duck) | else
|
|
raise PreProcessingError, _("Unknown core field '%s'") % field
|
|||
end
|
|||
else
|
|||
raise PreProcessingError, _("Unknown type '%s' for field '%s'") % [type, field]
|
|||
end
|
|||
end
|
|||
2bd92292 | Marc Dequènes (Duck) | def modify_field(field, op, val)
|
|
3d5fa735 | Marc Dequènes (Duck) | unless self.has_field?(field)
|
|
raise PreProcessingError, _("No such field '%s' in object '%s'") % [field, self.class.handle]
|
|||
end
|
|||
5ec5940f | Marc Dequènes (Duck) | # TODO: not so beautiful…
|
|
obj_info, obj_aspects = self.organized_data
|
|||
fields_info = obj_info.merge(obj_aspects.values.inject{|d1, d2| d1.merge(d2) } || {})
|
|||
attr_info = fields_info[field]
|
|||
a8bda213 | Marc Dequènes (Duck) | if attr_info[:read_only]
|
|
3d5fa735 | Marc Dequènes (Duck) | raise PreProcessingError, _("The field '%s' cannot be modified (read only)") % field
|
|
end
|
|||
5ec5940f | Marc Dequènes (Duck) | if ['read_only', 'forbidden'].include? attr_info[:access]
|
|
raise PreProcessingError, _("The field '%s' cannot be modified (forbidden)") % field
|
|||
end
|
|||
016427dd | Marc Dequènes (Duck) | if attr_info[:syntax] == "1.3.6.1.4.1.1466.115.121.1.12"
|
|
f43a4cb1 | Marc Dequènes (Duck) | if val =~ FULL_HANDLE_PATTERN
|
|
e99088e1 | Marc Dequènes (Duck) | obj_hdl = $1.downcase
|
|
item_hdl = $2
|
|||
obj_klass = self.class.shadow.get_object(obj_hdl)
|
|||
raise PreProcessingError, _("No such object '%s'") % obj_hdl if obj_klass.nil?
|
|||
begin
|
|||
item = obj_klass.find(item_hdl, :attributes => [''])
|
|||
rescue ActiveLdap::EntryNotFound
|
|||
raise PreProcessingError, _("No such item '%s/%s'") % [obj_klass.handle, item_hdl]
|
|||
end
|
|||
val = item.dn
|
|||
end
|
|||
5f929912 | Marc Dequènes (Duck) | else
|
|
val = self.schema.ldap_syntax(attr_info[:syntax]).normalize_value(val) unless attr_info[:syntax].nil?
|
|||
3d5fa735 | Marc Dequènes (Duck) | end
|
|
old_val = self.send(field, true)
|
|||
# if val is nil or the latest value is removed, then the attribute is removed from the object,
|
|||
case op
|
|||
when '='
|
|||
73a3d129 | Marc Dequènes (Duck) | val = [val] if old_val.is_a? Enumerable
|
|
3d5fa735 | Marc Dequènes (Duck) | return false if val == old_val
|
|
self.send(field + "=", val)
|
|||
when '+='
|
|||
if attr_info.single_value?
|
|||
raise PreProcessingError, _("The field '%s' cannot hold more than one value") % field
|
|||
end
|
|||
return false if old_val.include?(val)
|
|||
new_val = old_val << val
|
|||
self.send(field + "=", new_val)
|
|||
when '-='
|
|||
return false unless old_val.include?(val)
|
|||
new_val = old_val - [val]
|
|||
self.send(field + "=", new_val)
|
|||
else
|
|||
raise SyntaxError, _("Unknown operator '%s'") % op
|
|||
end
|
|||
true
|
|||
end
|
|||
2bd92292 | Marc Dequènes (Duck) | def modify_relation(rel, op, val)
|
|
3d5fa735 | Marc Dequènes (Duck) | unless self.relations.include?(rel)
|
|
raise PreProcessingError, _("No such relation '%s' for object '%s'") % [rel, self.class.handle]
|
|||
end
|
|||
5ec5940f | Marc Dequènes (Duck) | # TODO: not so beautiful…
|
|
obj_info, obj_aspects = self.organized_data
|
|||
fields_info = obj_info.merge(obj_aspects.values.inject{|d1, d2| d1.merge(d2) } || {})
|
|||
rel_info = fields_info['rel:' + rel]
|
|||
3d5fa735 | Marc Dequènes (Duck) | if rel_info[:read_only]
|
|
raise PreProcessingError, _("The relation '%s' cannot be modified (read only)") % rel
|
|||
end
|
|||
5ec5940f | Marc Dequènes (Duck) | if ['read_only', 'forbidden'].include? rel_info[:access]
|
|
raise PreProcessingError, _("The field '%s' cannot be modified (forbidden)") % rel
|
|||
end
|
|||
cad12da3 | Marc Dequènes (Duck) | if val.blank?
|
|
raise PreProcessingError, _("No item handle specified for relation '%s'") % rel
|
|||
end
|
|||
3d5fa735 | Marc Dequènes (Duck) | # fetch remote object in relation, which will be the real 'val'
|
|
foreign_item_list = rel_info[:foreign_klass].find(:all, val)
|
|||
if foreign_item_list.empty?
|
|||
raise PreProcessingError, _("Foreign item '%s' for relation '%s' not found") % [val, rel]
|
|||
end
|
|||
if foreign_item_list.size > 1
|
|||
raise WeirdError, _("Ambiguous item '%s' for relation '%s' (%s possible items)") %
|
|||
[val, rel, foreign_item_list.size]
|
|||
end
|
|||
old_val = self.send(rel)
|
|||
val = foreign_item_list.first
|
|||
# if val is nil or the latest value is removed, then the association's attribute is removed from one side
|
|||
case op
|
|||
when '='
|
|||
val = [val] if old_val.is_a? Enumerable
|
|||
self.send(rel + "=", val)
|
|||
when '+='
|
|||
if rel_info[:single_value]
|
|||
raise PreProcessingError, _("The relation '%s' cannot hold more than one foreign item") % rel
|
|||
end
|
|||
return false if old_val.include?(val)
|
|||
self.send(rel) << val
|
|||
when '-='
|
|||
return false unless old_val.include?(val)
|
|||
self.send(rel).delete(val)
|
|||
else
|
|||
raise SyntaxError, _("Unknown operator '%s'") % op
|
|||
end
|
|||
true
|
|||
end
|
|||
2bd92292 | Marc Dequènes (Duck) | def modify_aspects(op, aspect_name)
|
|
5b1a6342 | Marc Dequènes (Duck) | unless self.class.possible_aspects.include?(aspect_name)
|
|
raise PreProcessingError, _("No such aspect '%s' for object '%s'") % [aspect_name, self.class.handle]
|
|||
3d5fa735 | Marc Dequènes (Duck) | end
|
|
case op
|
|||
when '='
|
|||
raise PreProcessingError, _("The equality operator is not possible for aspects")
|
|||
when '+='
|
|||
2bd92292 | Marc Dequènes (Duck) | return false if self.aspects.keys.include?(aspect_name)
|
|
3d5fa735 | Marc Dequènes (Duck) | ||
2bd92292 | Marc Dequènes (Duck) | self.add_aspect(aspect_name)
|
|
3d5fa735 | Marc Dequènes (Duck) | ||
when '-='
|
|||
2bd92292 | Marc Dequènes (Duck) | return false unless self.aspects.keys.include?(aspect_name)
|
|
3d5fa735 | Marc Dequènes (Duck) | ||
2bd92292 | Marc Dequènes (Duck) | self.remove_aspect(aspect_name)
|
|
3d5fa735 | Marc Dequènes (Duck) | ||
else
|
|||
raise SyntaxError, _("Unknown operator '%s'") % op
|
|||
end
|
|||
true
|
|||
end
|
|||
7414a658 | Marc Dequènes (Duck) | def modify_parent(op, parent_full_handle)
|
|
case op
|
|||
fa116a1b | Marc Dequènes (Duck) | when '='
|
|
7414a658 | Marc Dequènes (Duck) | when '+=', '-='
|
|
raise PreProcessingError, _("This operator is not possible for parent")
|
|||
else
|
|||
raise SyntaxError, _("Unknown operator '%s'") % op
|
|||
end
|
|||
020c18fe | Marc Dequènes (Duck) | unless Manipulation.looks_like_full_handle?(parent_full_handle)
|
|
raise PreProcessingError, _("Parent for the item is not a full handle")
|
|||
end
|
|||
parent_item = Manipulation.find_item_by_full_handle(self.class.shadow, parent_full_handle)
|
|||
7414a658 | Marc Dequènes (Duck) | ||
if self.new_entry?
|
|||
98217996 | Marc Dequènes (Duck) | self.base = parent_item.dn - parent_item.class.base
|
|
7414a658 | Marc Dequènes (Duck) | else
|
|
raise PreProcessingError, _("Moving items is not yet implemented")
|
|||
end
|
|||
fa116a1b | Marc Dequènes (Duck) | ||
@parent_changed = true
|
|||
bf962db8 | Marc Dequènes (Duck) | @latest_parent_full_handle = parent_full_handle
|
|
fa116a1b | Marc Dequènes (Duck) | end
|
|
5b1a6342 | Marc Dequènes (Duck) | def add_aspect(aspect_name)
|
|
return unless self.class.possible_aspects.include?(aspect_name)
|
|||
d9091cc0 | Marc Dequènes (Duck) | ||
5b1a6342 | Marc Dequènes (Duck) | aspect_mapping = self.class.shadow.get_aspect(aspect_name).parameters[:mapping]
|
|
add_class(*aspect_mapping[:classes])
|
|||
13594999 | Marc Dequènes (Duck) | ||
# recursive dependency enforcement
|
|||
5b1a6342 | Marc Dequènes (Duck) | aspect_mapping[:depend_aspects].each do |dep_aspect|
|
|
13594999 | Marc Dequènes (Duck) | add_aspect(dep_aspect)
|
|
end
|
|||
d9091cc0 | Marc Dequènes (Duck) | end
|
|
5b1a6342 | Marc Dequènes (Duck) | def remove_aspect(aspect_name)
|
|
return unless self.class.possible_aspects.include?(aspect_name)
|
|||
d9091cc0 | Marc Dequènes (Duck) | ||
560c7b40 | Marc Dequènes (Duck) | # remove reverse-depends
|
|
self.class.possible_aspects.each do |rdep_aspect_name|
|
|||
if self.class.shadow.get_aspect(rdep_aspect_name).parameters[:mapping][:depend_aspects].include?(aspect_name)
|
|||
remove_aspect(rdep_aspect_name)
|
|||
end
|
|||
end
|
|||
5b1a6342 | Marc Dequènes (Duck) | aspect_mapping = self.class.shadow.get_aspect(aspect_name).parameters[:mapping]
|
|
remove_class(*aspect_mapping[:classes])
|
|||
d9091cc0 | Marc Dequènes (Duck) | end
|
|
3de546c5 | Marc Dequènes (Duck) | def delete(options = {})
|
|
7b832340 | Marc Dequènes (Duck) | before_delete_jobs
|
|
3de546c5 | Marc Dequènes (Duck) | super(options)
|
|
d8cfa085 | Marc Dequènes (Duck) | after_save_jobs
|
|
end
|
|||
def delete_recursive
|
|||
# TODO: recursive instanciation and reverse recursive hook calls
|
|||
7b832340 | Marc Dequènes (Duck) | before_delete_jobs
|
|
d8cfa085 | Marc Dequènes (Duck) | self.class.delete_all(nil, :scope => :sub, :base => self.dn)
|
|
after_delete_jobs
|
|||
dba6a81b | Marc Dequènes (Duck) | end
|
|
953a2b4d | Marc Dequènes (Duck) | # cannot override create_or_update() because of alias chaining
|
|
def save
|
|||
before_save_jobs
|
|||
r = super
|
|||
after_save_jobs
|
|||
r
|
|||
end
|
|||
89d8bebc | Marc Dequènes (Duck) | ||
953a2b4d | Marc Dequènes (Duck) | def save!
|
|
3de546c5 | Marc Dequènes (Duck) | before_save_jobs
|
|
r = super
|
|||
after_save_jobs
|
|||
r
|
|||
end
|
|||
c2c26953 | Marc Dequènes (Duck) | def method_missing(method, *args)
|
|
aspects.each_pair do |aspect_name, aspect|
|
|||
if aspect.instance_methods.include? method.to_s
|
|||
aspect_inst = @aspects_inst_cache[aspect_name]
|
|||
if aspect_inst.nil?
|
|||
aspect_inst = aspect.new(self)
|
|||
@aspects_inst_cache[aspect_name] = aspect_inst
|
|||
end
|
|||
return aspect_inst.send(method, *args)
|
|||
end
|
|||
end
|
|||
super
|
|||
end
|
|||
953a2b4d | Marc Dequènes (Duck) | protected
|
|
5aa80ef6 | Marc Dequènes (Duck) | def before_save_jobs
|
|
d8cfa085 | Marc Dequènes (Duck) | check_hooks_before(:save)
|
|
5aa80ef6 | Marc Dequènes (Duck) | check_missing_attributes
|
|
70b17bfa | Marc Dequènes (Duck) | check_password
|
|
fa116a1b | Marc Dequènes (Duck) | check_parent
|
|
5aa80ef6 | Marc Dequènes (Duck) | end
|
|
d8cfa085 | Marc Dequènes (Duck) | def before_delete_jobs
|
|
check_hooks_before(:delete)
|
|||
end
|
|||
def check_hooks_before(action)
|
|||
0fd15b39 | Marc Dequènes (Duck) | begin
|
|
d8cfa085 | Marc Dequènes (Duck) | case action
|
|
when :save
|
|||
if self.new_entry?
|
|||
0fd15b39 | Marc Dequènes (Duck) | self.class.hook_before_create(self)
|
|
d8cfa085 | Marc Dequènes (Duck) | else
|
|
0fd15b39 | Marc Dequènes (Duck) | self.class.hook_before_modify(self)
|
|
d8cfa085 | Marc Dequènes (Duck) | end
|
|
when :delete
|
|||
0fd15b39 | Marc Dequènes (Duck) | self.class.hook_before_delete(self)
|
|
end
|
|||
rescue
|
|||
raise PreProcessingError, _("Hook before action on object failed: %s") % $!
|
|||
end
|
|||
# TODO: move this in the LdapAspect class
|
|||
self.aspects.each do |aspect_name, aklass|
|
|||
begin
|
|||
case action
|
|||
when :save
|
|||
if self.new_entry?
|
|||
aklass.hook_before_create(self)
|
|||
else
|
|||
aklass.hook_before_modify(self)
|
|||
end
|
|||
when :delete
|
|||
aklass.hook_before_delete(self)
|
|||
end
|
|||
rescue
|
|||
raise PreProcessingError, _("Hook before action on aspect '%s' failed: %s") % [aspect_name, $!]
|
|||
dba6a81b | Marc Dequènes (Duck) | end
|
|
end
|
|||
end
|
|||
5aa80ef6 | Marc Dequènes (Duck) | def check_missing_attributes
|
|
missing_fields = self.missing_attributes
|
|||
unless missing_fields.empty?
|
|||
miss_str = []
|
|||
missing_fields.each do |field|
|
|||
str = Translator.translate_field_name(field)
|
|||
str += " [#{field}]" if $program_options[:handles]
|
|||
miss_str << str
|
|||
end
|
|||
raise PreProcessingError, _("Cannot save the item; the following fields are missing: %s") %
|
|||
miss_str.join(", ")
|
|||
end
|
|||
end
|
|||
70b17bfa | Marc Dequènes (Duck) | def check_password
|
|
return unless self.modified_attributes([:replace], true).include? 'userPassword'
|
|||
hash_func = self.class.config.global_config[:password_hash]
|
|||
return if hash_func.nil?
|
|||
self.userPassword = ActiveLdap::UserPassword.send(hash_func, self.userPassword)
|
|||
end
|
|||
fa116a1b | Marc Dequènes (Duck) | def check_parent
|
|
bf962db8 | Marc Dequènes (Duck) | parent_full_handle = nil
|
|
parent_item_dn = nil
|
|||
fa116a1b | Marc Dequènes (Duck) | if self.new_entry? and not self.parent_changed
|
|
parent_full_handle = self.class.parameters[:mapping][:default_parent]
|
|||
if parent_full_handle
|
|||
020c18fe | Marc Dequènes (Duck) | unless Manipulation.looks_like_full_handle?(parent_full_handle)
|
|
raise PreProcessingError, _("Default parent for the item is not a full handle")
|
|||
end
|
|||
fa116a1b | Marc Dequènes (Duck) | begin
|
|
020c18fe | Marc Dequènes (Duck) | parent_item = Manipulation.find_item_by_full_handle(self.class.shadow, parent_full_handle)
|
|
98217996 | Marc Dequènes (Duck) | self.base = parent_item.dn - parent_item.class.base
|
|
fa116a1b | Marc Dequènes (Duck) | rescue
|
|
raise PreProcessingError, _("Cannot create the item: bad default parent for this kind of object: %s") % $!
|
|||
end
|
|||
bf962db8 | Marc Dequènes (Duck) | ||
parent_item_dn = parent_item.dn.to_s
|
|||
fa116a1b | Marc Dequènes (Duck) | else
|
|
raise PreProcessingError, _("Cannot create the item: parent not specified and no default for such object")
|
|||
end
|
|||
end
|
|||
bf962db8 | Marc Dequènes (Duck) | ||
parent_full_handle = @latest_parent_full_handle if parent_full_handle.nil?
|
|||
adf367f7 | Marc Dequènes (Duck) | # return if this item is not new and was not relocated
|
|
return if parent_full_handle.nil?
|
|||
bf962db8 | Marc Dequènes (Duck) | parent_item_dn = self.base.to_s if parent_item_dn.nil?
|
|
p_hdl_restr = self.class.parameters[:mapping][:parent_handle_restrictions]
|
|||
unless p_hdl_restr.nil? or parent_full_handle =~ Regexp.new(p_hdl_restr)
|
|||
raise PreProcessingError, _("This parent can't raise such a child (handle restrictions)")
|
|||
end
|
|||
p_dn_restr = self.class.parameters[:mapping][:parent_dn_restrictions]
|
|||
unless p_dn_restr.nil? or parent_item_dn =~ Regexp.new(p_dn_restr)
|
|||
raise PreProcessingError, _("This parent can't raise such a child (DN restrictions)")
|
|||
end
|
|||
fa116a1b | Marc Dequènes (Duck) | end
|
|
d8cfa085 | Marc Dequènes (Duck) | def after_save_jobs
|
|
check_hooks_after(:save)
|
|||
fa116a1b | Marc Dequènes (Duck) | @parent_changed = false
|
|
d8cfa085 | Marc Dequènes (Duck) | end
|
|
def after_delete_jobs
|
|||
check_hooks_after(:delete)
|
|||
end
|
|||
def check_hooks_after(action)
|
|||
0a4afc42 | Marc Dequènes (Duck) | # TODO: move this in the LdapAspect class
|
|
72316144 | Marc Dequènes (Duck) | self.aspects.each do |aspect_name, aklass|
|
|
0fd15b39 | Marc Dequènes (Duck) | begin
|
|
case action
|
|||
when :save
|
|||
if self.new_entry?
|
|||
aklass.hook_after_create(self)
|
|||
else
|
|||
aklass.hook_after_modify(self)
|
|||
end
|
|||
when :delete
|
|||
aklass.hook_after_delete(self)
|
|||
end
|
|||
rescue
|
|||
raise PreProcessingError, _("Hook after action on aspect '%s' failed: %s") % [aspect_name, $!]
|
|||
end
|
|||
end
|
|||
d8cfa085 | Marc Dequènes (Duck) | ||
0fd15b39 | Marc Dequènes (Duck) | begin
|
|
d8cfa085 | Marc Dequènes (Duck) | case action
|
|
when :save
|
|||
if self.new_entry?
|
|||
0fd15b39 | Marc Dequènes (Duck) | self.class.hook_after_create(self)
|
|
d8cfa085 | Marc Dequènes (Duck) | else
|
|
0fd15b39 | Marc Dequènes (Duck) | self.class.hook_after_modify(self)
|
|
d8cfa085 | Marc Dequènes (Duck) | end
|
|
when :delete
|
|||
0fd15b39 | Marc Dequènes (Duck) | self.class.hook_after_delete(self)
|
|
7b832340 | Marc Dequènes (Duck) | end
|
|
0fd15b39 | Marc Dequènes (Duck) | rescue
|
|
raise PreProcessingError, _("Hook after action on object failed: %s") % $!
|
|||
7b832340 | Marc Dequènes (Duck) | end
|
|
d8cfa085 | Marc Dequènes (Duck) | end
|
|
66e78c7a | Marc Dequènes (Duck) | def attribute_parameters(attr, claimed_by)
|
|
c1f6ab89 | Marc Dequènes (Duck) | obj = claimed_by.is_a?(Class) ? claimed_by : claimed_by.class
|
|
66e78c7a | Marc Dequènes (Duck) | self.class.shadow.parameters[:interface][:attribute_defaults].dup.
|
|
c1f6ab89 | Marc Dequènes (Duck) | recursive_merge(obj.parameters[:interface][:attribute_defaults]).
|
|
66e78c7a | Marc Dequènes (Duck) | recursive_merge(self.class.shadow.parameters[:interface][:attributes][attr.to_sym] || {}).
|
|
c1f6ab89 | Marc Dequènes (Duck) | recursive_merge(obj.parameters[:interface][:attributes][attr.to_sym] || {})
|
|
66e78c7a | Marc Dequènes (Duck) | end
|
|
a8bda213 | Marc Dequènes (Duck) | ||
66e78c7a | Marc Dequènes (Duck) | def fetch_attribute_data(attr, claimed_by)
|
|
base_info = self.attribute_info(attr)
|
|||
return nil if base_info.nil?
|
|||
attr_data = attribute_parameters(attr, claimed_by)
|
|||
7597d4fb | Marc Dequènes (Duck) | attr_data.merge!(base_info)
|
|
66e78c7a | Marc Dequènes (Duck) | ||
val = (attr == 'objectClass') ? self.classes : self.send(attr)
|
|||
attr_data.merge!({
|
|||
:value => val,
|
|||
:multiple => val.is_a?(Array)
|
|||
})
|
|||
attr_data
|
|||
89d8bebc | Marc Dequènes (Duck) | end
|
|
66e78c7a | Marc Dequènes (Duck) | def fetch_relation_data(rel, claimed_by)
|
|
data = self.send(rel)
|
|||
if data.is_a? Enumerable
|
|||
if data.empty?
|
|||
value = nil
|
|||
3d58e226 | Marc Dequènes (Duck) | else
|
|
66e78c7a | Marc Dequènes (Duck) | value = data.collect{|g| g.handle }
|
|
multiple = true
|
|||
3d58e226 | Marc Dequènes (Duck) | end
|
|
66e78c7a | Marc Dequènes (Duck) | else
|
|
# the exists? method also ensure the object is loaded
|
|||
if data.exists?
|
|||
value = data.handle
|
|||
f7217dcd | Marc Dequènes (Duck) | else
|
|
66e78c7a | Marc Dequènes (Duck) | value = nil
|
|
f7217dcd | Marc Dequènes (Duck) | end
|
|
66e78c7a | Marc Dequènes (Duck) | multiple = false
|
|
end
|
|||
if value.nil?
|
|||
nil
|
|||
else
|
|||
1db3a734 | Marc Dequènes (Duck) | attr_data = attribute_parameters(rel, claimed_by)
|
|
5ec5940f | Marc Dequènes (Duck) | attr_data.merge!(self.class.relations_info[rel.to_sym])
|
|
66e78c7a | Marc Dequènes (Duck) | ||
attr_data.merge!({
|
|||
:syntax => nil,
|
|||
:value => value,
|
|||
:single_value => !multiple,
|
|||
:multiple => multiple,
|
|||
:binary => false,
|
|||
:operational => false
|
|||
})
|
|||
attr_data
|
|||
89d8bebc | Marc Dequènes (Duck) | end
|
|
end
|
|||
end
|
|||
6d235fb6 | Marc Dequènes (Duck) | ||
class ExternalLdapObject < ActiveLdap::Base
|
|||
self.base = ''
|
|||
end
|
|||
89d8bebc | Marc Dequènes (Duck) | end
|
|
5a48a521 | Marc Dequènes (Duck) | end
|