Revision b52f0f7d
Added by Marc Dequènes over 15 years ago
- ID b52f0f7dd2e38270b9d123a28907deeba34dd249
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
[evol] move all activeldap workarounds away, and properly manage operational attributes (using schema instead of a hardcoded list)