Project

General

Profile

Download (14.2 KB) Statistics
| Branch: | Tag: | Revision:
#!/usr/bin/ruby -Ku

#--
# LdapShadows, a Medium-level LDAP Access Library and Tool.
# Copyright (c) 2009 Marc Dequènes (Duck) <Duck@DuckCorp.org>
#
# 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/>.
#++

# to allow in-place run for test
$: << File.join(File.dirname(__FILE__), "..", "lib")

# get locale from shell
locale = ENV['LANGUAGE'] || ENV['LC_ALL'] || ENV['LC_MESSAGES'] || ENV['LANG']

require 'ldap_shadows'
require 'ldap_shadows/display_helper'
require 'ldap_shadows/manipulation_helper'
require 'cmdparse2'

include LdapShadows

set_locale(locale)


# defaults
$program_options = {
:admin => false,
:debug => false,
:expert => false,
:handles => false,
:shadow_name => nil
}

cmdparser = CmdParse::CommandParser.new(true)
cmdparser.banner = PRODUCT
cmdparser.program_name = File.basename(__FILE__)
cmdparser.program_version = VERSION.split(".")

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 }
opt.on("-s SHADOW", "--shadow SHADOW", "Choose shadow to travel through") {|t| $program_options[:shadow_name] = t }
end

cmdparser.add_command(CmdParse::HelpCommand.new)
cmdparser.add_command(CmdParse::VersionCommand.new)


class Command < CmdParse::Command
attr_accessor :usages_params

def usage
if @usages_params
base_usage_str = super.sub("[ARGS]", "")
usages = @usages_params.collect {|params| base_usage_str + params }
usages.join("\n")
else
super
end
end

def execute(args)
config = LdapShadows::Config.instance
config.load_global_config([File.join(ENV['HOME'], ".shadowwalker")])
begin
$shadow = config.load_shadow($program_options[:shadow_name])
rescue ActiveLdap::Error => e
STDERR.puts _("LDAP connection error: %s") % e.to_s
exit 2
rescue
STDERR.puts _("Shadow initialization failed: %s") % $!
exit 2
end
end

protected

def params_shift_item_handle_full_handle(args, fetch_item = true, name = "item full-handle")
raise SyntaxError, _("no %s given") % name if args.empty?

full_handle = args.shift.downcase
raise SyntaxError, _("bad %s") % name unless full_handle =~ /^([a-zA-Z]+)\/(.+)$/
obj_hdl = $1
item_hdl = $2

split_args = [obj_hdl, item_hdl]
obj_klass = params_shift_object_handle(split_args)
if fetch_item
item = params_shift_item_handle(obj_klass, split_args)
[obj_klass, item]
else
[obj_klass, item_hdl]
end
end

def params_shift_object_handle(args)
raise SyntaxError, _("no object name given") if args.empty?

obj_hdl = args.shift.downcase.singularize
obj_klass = $shadow.get_object(obj_hdl)
raise PreProcessingError, _("No such object '%s'") % obj_hdl if obj_klass.nil?
obj_klass
end

def params_shift_item_handle(obj_klass, args)
raise SyntaxError, _("no item name given") if args.empty?

attr_list = ["*"]
attr_list << "+" if $program_options[:admin]

item_hdl = args.shift.downcase
begin
item = obj_klass.find(item_hdl, :attributes => attr_list)
rescue ActiveLdap::EntryNotFound
raise PreProcessingError, _("No such item '%s/%s'") % [obj_klass.handle, item_hdl]
end
item
end
end

class ListCommand < Command
def initialize
super('list', false)

self.short_desc = "list objects, and items/aspects/fields of an object"
self.usages_params = [
":objects",
"<object>",
"<object> :aspects",
"<object> :fields"
]
end

def execute(args)
super

raise SyntaxError, _("no object name given") if args.empty?

obj_hdl = args.shift.downcase.singularize
case obj_hdl
when ':object'
list = $shadow.objects
puts "=== List of LDAP objects (#{list.size}) ==="
list.each do |obj_name|
puts " - #{obj_name}"
end
else
obj_klass = params_shift_object_handle([obj_hdl])

if args.empty?
obj_human_name = Translator.translate_object_name(obj_hdl)
Display.display_item_list("List of #{obj_human_name.pluralize}", obj_klass.find(:all), $program_options)
else
subobj_hdl = args.shift.downcase.singularize

obj_human_name = Translator.translate_object_name(obj_hdl)

case subobj_hdl
when ':aspect'
list = obj_klass.possible_aspects
puts "=== List of LDAP Aspects of #{obj_human_name.pluralize} (#{list.size}) ==="
list.each do |aspect|
str = Translator.translate_aspect_name(aspect)
str += " [#{aspect}]" if $program_options[:handles]
puts " - #{str}"
end
when ':field'
list = obj_klass.possible_attributes
puts "=== List of LDAP Fields of #{obj_human_name.pluralize} (#{list.size}) ==="
list.each do |field|
str = Translator.translate_field_name(field)
str += " [#{field}]" if $program_options[:handles]
puts " - #{str}"
end
obj_klass.possible_aspects.each do |aspect|
aspect_human_name = Translator.translate_aspect_name(aspect.handle)
puts "--- Sublist of LDAP Fields for Aspect #{aspect_human_name} (#{list.size}) ---"
aspect.possible_attributes.each do |field|
str = Translator.translate_field_name(field)
str += " [#{field}]" if $program_options[:handles]
puts " - #{str}"
end
end
else
raise PreProcessingError, _("No such subobject '%s'") % subobj_hdl
end
end
end
end
end
cmdparser.add_command(ListCommand.new)

class ShowCommand < Command
def initialize
super('show', false)

self.short_desc = "show item information"
self.usages_params = [
"<object>/<item>",
"<object>/<item> <field>#? [<filename>]",
"<object>/<item> <field>[#<index>] [<filename>]"
]
self.options = CmdParse::OptionParserWrapper.new do |opt|
opt.on( '-f', '--family', 'Display family members.' ) { $program_options[:show_family_members] = true }
end
end

def execute(args)
super

obj_klass, item = params_shift_item_handle_full_handle(args)

if args.empty?
$program_options[:skip_binary] = true
Display.display_item(item, $program_options)
else
field_info = args.shift
field_name, file_no = field_info.split("#")

unless item.has_field?(field_name)
raise PreProcessingError, _("No such field '%s' in object '%s'") % [field_name, obj_klass.handle]
end
unless item.attribute_present?(field_name)
raise PreProcessingError, _("Field '%s' in item '%s' is not present") %
[field_name, item.full_handle]
end

field_data = item.send(field_name, true)

result_is_binary = false
if file_no == "?"
result = field_data.size
else
file_no_i = file_no.to_i
file_no = (file_no_i.to_s == file_no) ? file_no_i : nil

attr_info = ActiveLdap::Base.schema.attribute(field_name)

field_single = attr_info.single_value?
if file_no.nil? and not field_single
raise PreProcessingError, _("Field '%s' in object '%s' has multiple values, but you didn't select one") %
[field_name, obj_klass.handle]
end

file_no = 0 if file_no.nil?
if file_no > 0 and field_single
raise PreProcessingError, _("Field '%s' in object '%s' has only a single value") %
[field_name, obj_klass.handle]
end
if file_no < 0 or file_no >= field_data.size
raise PreProcessingError, _("Field '%s' in object '%s' doesn't have such file number '%s' (select in range [0, %s])") %
[field_name, obj_klass.handle, file_no, field_data.size - 1]
end

result = field_data[file_no]
result_is_binary = attr_info.binary?

unless result_is_binary or $program_options[:debug]
result = LdapShadows::Manipulation.interpret_field_value($shadow, attr_info.syntax.to_param, result)
end
end

if args.empty?
if result_is_binary
# output binary data without newline, so it can be pipped or redirected easily
STDOUT.write result
else
puts result
end
else
file_name = args.shift
if File.exists?(file_name)
raise PreProcessingError, _("File '%s' already exists, and i won't overwrite it") % file_name
end

begin
open(file_name, "w") do |fp|
fp.write result
end
rescue
raise ProcessingError, _("Cannot save file: %s") % $!
end

puts "File saved."
end
end
end
end
cmdparser.add_command(ShowCommand.new)

class TreeCommand < Command
def initialize
super('tree', false)

self.short_desc = "show skeleton items tree"
self.usages_params = [
"[<location-item-full-handle>]"
]
self.options = CmdParse::OptionParserWrapper.new do |opt|
opt.on( '-a', '--all', 'Display all objects in the tree.' ) { $program_options[:tree_all_objects] = true }
end
end

def execute(args)
super

puts "Tree:"

search_base = nil
unless args.empty?
loc_obj_klass, loc_item = params_shift_item_handle_full_handle(args, true, "location full-handle")
search_base = loc_item.dn
end

tree = $shadow.tree(search_base, $program_options[:debug], $program_options[:tree_all_objects])

Display.display_hash_tree(tree, 0)
end
end
cmdparser.add_command(TreeCommand.new)

class ModifyCommand < Command
def initialize
super('modify', false)

self.short_desc = "Modify attributes of an item"
self.usages_params = [
"<item-full-handle> (<field>|<relation>|:aspects)(=|+=|-=)<value> [(<field>|<relation>|:aspects|:parent)(=|+=|-=)<value>] ..."
]
end

def execute(args)
super

obj_klass, item = params_shift_item_handle_full_handle(args)

modification_done = LdapShadows::Manipulation.item_modify_from_strings(item, args)

if modification_done
item.save!
puts "Modification done."
else
puts "Nothing to do."
end
end
end
cmdparser.add_command(ModifyCommand.new)

class DeleteCommand < Command
def initialize
super('delete', false)

self.short_desc = "Delete an item"
self.usages_params = [
"<item-full-handle>"
]
self.options = CmdParse::OptionParserWrapper.new do |opt|
opt.on( '-r', '--recursive', 'Delete object and all children recursively.' ) { $program_options[:recursive_delete] = true }
end
end

def execute(args)
super

obj_klass, item = params_shift_item_handle_full_handle(args)

if $program_options[:recursive_delete]
item.delete_recursive
else
item.delete
end

puts "Deletion done."
end
end
cmdparser.add_command(DeleteCommand.new)

class CreateCommand < Command
def initialize
super('create', false)

self.short_desc = "Create an item"
self.usages_params = [
"<new-item-full-handle> <parent-item-full-handle> [<field>=<value>|<relation>|:aspects|:parent)(=|+=|-=)<value>] ..."
]
end

def execute(args)
super

obj_klass, item_hdl = params_shift_item_handle_full_handle(args, false)
item = obj_klass.new(item_hdl)

modification_done = LdapShadows::Manipulation.item_modify_from_strings(item, args)

# TODO: use default parent for such object if defined (-> predefined hook)
# TODO: detect if parent specified and allowed (-> predefined hook)
item.save!
puts "Creation done."
end
end
cmdparser.add_command(CreateCommand.new)

class SearchCommand < Command
def initialize
super('search', false)

self.short_desc = "Search items"
self.usages_params = [
"[:objects=<object>[,<object>]...] [:aspects=<aspect>[,<aspect>]...] [<field>=<value>] ..."
]
end

def execute(args)
super

raise SyntaxError, _("no search arguments") if args.empty?

res = LdapShadows::Manipulation.items_find_from_strings($shadow, args)
unless res.empty?
display_lines = []
res.each do |raw_item|
display_lines << LdapShadows::Manipulation.raw_item_info($shadow, raw_item)[:name]
end
puts display_lines.join("\n")
end
end
end
cmdparser.add_command(SearchCommand.new)


begin
cmdparser.parse
rescue SyntaxError => e
STDERR.puts _("Syntax error: %s") % e.to_s
exit 1
rescue PreProcessingError => e
STDERR.puts _("Preprocessing error: %s") % e.to_s
exit 2
rescue ActiveLdap::EntryInvalid => e
# work around activeldap#26758 (for english output only :-/)
STDERR.puts e.to_s.sub(/invalid format: .*: required syntax:/, "invalid format. required syntax:")
exit 3
rescue ActiveLdap::OperationNotPermitted => e
STDERR.puts _("You don't have enough rights for this operation")
exit 3
rescue ActiveLdap::Error => e
STDERR.puts _("LDAP error: %s") % e.to_s
exit 3
rescue ProcessingError => e
STDERR.puts _("Processing error: %s") % e.to_s
exit 3
rescue WeirdError =>e
STDERR.puts _("Weird error: %s") % e.to_s
exit 4
end
    (1-1/1)