Project

General

Profile

« Previous | Next » 

Revision b52f0f7d

Added by Marc Dequènes over 15 years ago

  • ID b52f0f7dd2e38270b9d123a28907deeba34dd249

[evol] move all activeldap workarounds away, and properly manage operational attributes (using schema instead of a hardcoded list)

View differences:

bin/shadowwalker
cmdparser.options = CmdParse::OptionParserWrapper.new do |opt|
opt.separator "Global options:"
opt.on("-a", "--admin", "Output extra info for administrators") {|t| $program_options[:admin] = true }
opt.on("-d", "--debug", "Output debug info without being formated") {|t| $program_options[:debug] = true }
opt.on("-e", "--expert", "Output extra info for expert users") {|t| $program_options[:expert] = true }
opt.on("-H", "--handles", "Output with handles (objects/field/... keys used for manipulations)") {|t| $program_options[:handles] = true }
......
obj_klass
end
def params_shift_item(obj_klass, args, with_expert = false)
def params_shift_item(obj_klass, args)
raise SyntaxError, _("no item name given") if args.empty?
attr_list = ["*"]
attr_list << "+" if $program_options[:expert] and with_expert # add operational attributes if expert
attr_list << "+" if $program_options[:admin]
item_hdl = args.shift.downcase
begin
......
def execute (args)
obj_klass = params_shift_object(args)
item = params_shift_item(obj_klass, args, true)
item = params_shift_item(obj_klass, args)
if args.empty?
$program_options[:skip_binary] = true
conf/ldap_shadows/test.conf
---
presentation:
hidden_attributes: ['objectClass', 'userPassword']
hidden_attributes: ['objectClass', 'userPassword', 'hasSubordinates', 'entryUUID', 'entryDN', 'structuralObjectClass', 'subschemaSubentry']
tree_objects: ['unit', 'entity']
objects:
bot:
lib/ldap_shadows/activeldap_fixes.rb
module ActiveLdap
# patch for activeldap#26824
class Schema
class Attribute
# operational?
#
# Returns true if an attribute is operational
# USAGE contains directoryOperation
# Duck: new method
def operational?
@operational
end
def collect_info
@description = attribute("DESC")[0]
@super_attribute = attribute("SUP")[0]
if @super_attribute
@super_attribute = @schema.attribute(@super_attribute)
@super_attribute = nil if @super_attribute.id.nil?
end
@read_only = attribute('NO-USER-MODIFICATION')[0] == 'TRUE'
@single_value = attribute('SINGLE-VALUE')[0] == 'TRUE'
@syntax = attribute("SYNTAX")[0]
@syntax = @schema.ldap_syntax(@syntax) if @syntax
if @syntax
@binary_required = @syntax.binary_transfer_required?
@binary = (@binary_required or !@syntax.human_readable?)
@derived_syntax = @syntax
else
@binary_required = false
@binary = false
@derived_syntax = nil
@derived_syntax = @super_attribute.syntax if @super_attribute
end
# Duck: newly collected data
@operational = attribute("USAGE").include?("directoryOperation")
end
end
end
# patch for activeldap#26745
class Base
def collect_modified_attributes(ldap_data, data)
attributes = []
# Now that all the options will be treated as unique attributes
# we can see what's changed and add anything that is brand-spankin'
# new.
ldap_data.each do |k, v|
next if schema.attribute(k).read_only?
value = data[k] || []
next if v == value
x = value
value = self.class.remove_blank_value(value) || []
next if v == value
# Create mod entries
if self.class.blank_value?(value)
# Since some types do not have equality matching rules,
# delete doesn't work
# Replacing with nothing is equivalent.
if !data.has_key?(k) and schema.attribute(k).binary_required?
value = [{'binary' => []}]
end
else
# Ditched delete then replace because attribs with no equality
# match rules will fails
end
attributes.push([:replace, k, value])
end
data.each do |k, v|
value = v || []
next if ldap_data.has_key?(k)
value = self.class.remove_blank_value(value) || []
next if self.class.blank_value?(value)
# Detect subtypes and account for them
# REPLACE will function like ADD, but doesn't hit EQUALITY problems
# TODO: Added equality(attr) to Schema
attributes.push([:replace, k, value])
end
attributes
end
end
# unfinished workaround for activeldap#26720 (not sure it would work in all cases)
module Validations
def validate_required_ldap_values
_schema = nil
# Make sure all MUST attributes have a value
entry_attribute.object_classes.each do |object_class|
object_class.must.each do |required_attribute|
# Normalize to ensure we catch schema problems
# needed?
real_name = to_real_attribute_name(required_attribute.name, true)
raise UnknownAttribute.new(required_attribute) if real_name.nil?
next if required_attribute.read_only?
value = @data[real_name] || []
next unless self.class.blank_value?(value)
# Duck: ignore attributes which were not present, as they may not
# be available because of ACL
next unless @initial_attribute_list.nil? or @initial_attribute_list.include?(real_name)
_schema ||= schema
aliases = required_attribute.aliases.collect do |name|
self.class.human_attribute_name(name)
end
args = [self.class.human_object_class_name(object_class)]
if aliases.empty?
format = _("%{fn} is required attribute by objectClass '%s'")
else
format = _("%{fn} is required attribute by objectClass " \
"'%s': aliases: %s")
args << aliases.join(', ')
end
unless ActiveLdap.get_text_supported?
format = format.sub(/^%\{fn\} /, '')
end
errors.add(real_name, format % args)
end
end
end
# Duck: new method, should be hooked somewhere
def load_initial_attribute_list
@initial_attribute_list ||= self.nonempty_attributes
end
end
end
lib/ldap_shadows/controller.rb
$KCODE = 'UTF8'
require 'jcode'
require 'active_ldap'
require 'ldap_shadows/activeldap_fixes'
require 'ldap_shadows/object'
lib/ldap_shadows/display_utils.rb
def self.display_fields(attr_data, options = {})
attr_data.each_pair do |key, val|
next if val[:expert] and not options[:expert]
next if val[:admin] and not options[:admin]
field_name = Translator.translate_field_name(key)
lib/ldap_shadows/object.rb
ldap_mapping :prefix => '', :classes => ['top'], :scope => :sub
OPERATIONAL_ATTRIBUTES = ['creatorsName', 'createTimestamp', 'modifiersName', 'modifyTimestamp', 'structuralObjectClass', 'entryUUID']
# temporary method until active_ldap is fixed: return a DN object (see activeldap#23932)
def dn_obj
ActiveLdap::DistinguishedName.parse(self.dn)
......
has_attribute?(field)
end
def validate_required_ldap_values
_schema = nil
# Make sure all MUST attributes have a value
entry_attribute.object_classes.each do |object_class|
object_class.must.each do |required_attribute|
# Normalize to ensure we catch schema problems
# needed?
real_name = to_real_attribute_name(required_attribute.name, true)
raise UnknownAttribute.new(required_attribute) if real_name.nil?
next if required_attribute.read_only?
value = @data[real_name] || []
next unless self.class.blank_value?(value)
# workaround for activeldap#26720 (not sure it works in all cases)
next unless @initial_attribute_list.nil? or @initial_attribute_list.include?(real_name)
_schema ||= schema
aliases = required_attribute.aliases.collect do |name|
self.class.human_attribute_name(name)
end
args = [self.class.human_object_class_name(object_class)]
if aliases.empty?
format = _("%{fn} is required attribute by objectClass '%s'")
else
format = _("%{fn} is required attribute by objectClass " \
"'%s': aliases: %s")
args << aliases.join(', ')
end
unless ActiveLdap.get_text_supported?
format = format.sub(/^%\{fn\} /, '')
end
errors.add(real_name, format % args)
end
end
end
def human_name
attr_list = ['displayName', 'cn']
attr_list.unshift(self.class.presentation[:name_attribute]) if self.class.presentation.has_key?(:name_attribute)
......
end
def organized_data
expert_attributes = (self.class.presentation[:expert_attributes] || []) + OPERATIONAL_ATTRIBUTES
ignored_attrs = self.mapper.get_global_config[:hidden_attributes] || []
ignored_attrs += self.class.presentation[:hidden_attributes] || []
attr_list = self.nonempty_attributes - ignored_attrs
expert_attributes = (self.class.presentation[:expert_attributes] || [])
admin_attributes = attr_list.select do |attr|
ActiveLdap::Base.schema.attribute(attr).operational?
end
aspects = self.aspects
rel_list = self.possible_relations
......
taken_attr_list = aspect_data[:presentation][:associated_attributes] & (attr_list + ignored_attrs)
unless taken_attr_list.empty?
obj_aspects[aspect] ||= {}
obj_aspects[aspect].merge!(fetch_attributes_data(taken_attr_list, expert_attributes))
obj_aspects[aspect].merge!(fetch_attributes_data(taken_attr_list, expert_attributes, admin_attributes))
attr_list -= taken_attr_list
end
end
......
if self.class.presentation[:associate_unclaimed_attributes]
taken_attr_list = attr_list
else
taken_attr_list = []
taken_attr_list = admin_attributes
if self.class.presentation.has_key?(:associated_attributes)
taken_attr_list += self.class.presentation[:associated_attributes]
end
taken_attr_list += self.class.possible_attributes
end
taken_attr_list = taken_attr_list.uniq & attr_list
obj_info = fetch_attributes_data(taken_attr_list, expert_attributes)
obj_info = fetch_attributes_data(taken_attr_list, expert_attributes, admin_attributes)
attr_list -= taken_attr_list
# manage general relations
......
aspects.each do |aspect|
taken_attr_list = (self.class.possible_attributes_for_aspect(aspect) & attr_list)
obj_aspects[aspect] ||= {}
obj_aspects[aspect].merge!(fetch_attributes_data(taken_attr_list, expert_attributes))
obj_aspects[aspect].merge!(fetch_attributes_data(taken_attr_list, expert_attributes, admin_attributes))
attr_list -= taken_attr_list
break if attr_list.empty?
......
end
end
def fetch_attributes_data(attr_list, expert_attributes)
def fetch_attributes_data(attr_list, expert_attributes, admin_attributes)
attr_data = self.attributes.collect do |key, val|
if attr_list.include?(key)
[key, {
:value => val,
:multiple => (val.is_a?(Array) ? val.size : 1),
:expert => expert_attributes.include?(key),
:admin => admin_attributes.include?(key),
:binary => ActiveLdap::Base.schema.attribute(key).binary?
}]
else
......
:value => value,
:multiple => multiple,
:expert => expert_attributes.include?(rel_key),
:admin => false,
:binary => false
}]
end
end
Hash[rel_data.compact]
end
def load_initial_attribute_list
@initial_attribute_list ||= self.nonempty_attributes
end
end
end

Also available in: Unified diff