ruby-activeldap 0.7.4 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +375 -0
- data/COPYING +340 -0
- data/LICENSE +58 -0
- data/Manifest.txt +33 -0
- data/README +63 -0
- data/Rakefile +37 -0
- data/TODO +31 -0
- data/benchmark/bench-al.rb +152 -0
- data/lib/{activeldap.rb → active_ldap.rb} +280 -263
- data/lib/active_ldap/adaptor/base.rb +29 -0
- data/lib/active_ldap/adaptor/ldap.rb +466 -0
- data/lib/active_ldap/association/belongs_to.rb +38 -0
- data/lib/active_ldap/association/belongs_to_many.rb +40 -0
- data/lib/active_ldap/association/collection.rb +80 -0
- data/lib/active_ldap/association/has_many.rb +48 -0
- data/lib/active_ldap/association/has_many_wrap.rb +56 -0
- data/lib/active_ldap/association/proxy.rb +89 -0
- data/lib/active_ldap/associations.rb +162 -0
- data/lib/active_ldap/attributes.rb +199 -0
- data/lib/active_ldap/base.rb +1343 -0
- data/lib/active_ldap/callbacks.rb +19 -0
- data/lib/active_ldap/command.rb +46 -0
- data/lib/active_ldap/configuration.rb +96 -0
- data/lib/active_ldap/connection.rb +137 -0
- data/lib/{activeldap → active_ldap}/ldap.rb +1 -1
- data/lib/active_ldap/object_class.rb +70 -0
- data/lib/active_ldap/schema.rb +258 -0
- data/lib/{activeldap → active_ldap}/timeout.rb +0 -0
- data/lib/{activeldap → active_ldap}/timeout_stub.rb +0 -0
- data/lib/active_ldap/user_password.rb +92 -0
- data/lib/active_ldap/validations.rb +78 -0
- data/rails/plugin/active_ldap/README +54 -0
- data/rails/plugin/active_ldap/init.rb +6 -0
- data/test/TODO +2 -0
- data/test/al-test-utils.rb +337 -0
- data/test/command.rb +62 -0
- data/test/config.yaml +8 -0
- data/test/config.yaml.sample +6 -0
- data/test/run-test.rb +17 -0
- data/test/test-unit-ext.rb +2 -0
- data/test/test_associations.rb +334 -0
- data/test/test_attributes.rb +71 -0
- data/test/test_base.rb +345 -0
- data/test/test_base_per_instance.rb +32 -0
- data/test/test_bind.rb +53 -0
- data/test/test_callback.rb +35 -0
- data/test/test_connection.rb +38 -0
- data/test/test_connection_per_class.rb +50 -0
- data/test/test_find.rb +36 -0
- data/test/test_groupadd.rb +50 -0
- data/test/test_groupdel.rb +46 -0
- data/test/test_groupls.rb +107 -0
- data/test/test_groupmod.rb +51 -0
- data/test/test_lpasswd.rb +75 -0
- data/test/test_object_class.rb +32 -0
- data/test/test_reflection.rb +173 -0
- data/test/test_schema.rb +166 -0
- data/test/test_user.rb +209 -0
- data/test/test_user_password.rb +93 -0
- data/test/test_useradd-binary.rb +59 -0
- data/test/test_useradd.rb +55 -0
- data/test/test_userdel.rb +48 -0
- data/test/test_userls.rb +86 -0
- data/test/test_usermod-binary-add-time.rb +62 -0
- data/test/test_usermod-binary-add.rb +61 -0
- data/test/test_usermod-binary-del.rb +64 -0
- data/test/test_usermod-lang-add.rb +57 -0
- data/test/test_usermod.rb +56 -0
- data/test/test_validation.rb +38 -0
- metadata +94 -21
- data/lib/activeldap/associations.rb +0 -170
- data/lib/activeldap/base.rb +0 -1456
- data/lib/activeldap/configuration.rb +0 -59
- data/lib/activeldap/schema2.rb +0 -217
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'active_record/callbacks'
|
2
|
+
|
3
|
+
module ActiveLdap
|
4
|
+
module Callbacks
|
5
|
+
def self.append_features(base)
|
6
|
+
super
|
7
|
+
|
8
|
+
base.class_eval do
|
9
|
+
include ActiveRecord::Callbacks
|
10
|
+
|
11
|
+
def callback(method)
|
12
|
+
super
|
13
|
+
rescue ActiveRecord::ActiveRecordError
|
14
|
+
raise Error, $!.message
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module ActiveLdap
|
5
|
+
module Command
|
6
|
+
module_function
|
7
|
+
def parse_options(argv=nil, version=nil)
|
8
|
+
argv ||= ARGV.dup
|
9
|
+
options = OpenStruct.new
|
10
|
+
opts = OptionParser.new do |opts|
|
11
|
+
yield(opts, options)
|
12
|
+
|
13
|
+
opts.separator ""
|
14
|
+
opts.separator "Common options:"
|
15
|
+
|
16
|
+
opts.on_tail("--config=CONFIG",
|
17
|
+
"Specify configuration file written as YAML") do |file|
|
18
|
+
require 'yaml'
|
19
|
+
config = YAML.load(File.read(file)).symbolize_keys
|
20
|
+
Configuration::DEFAULT_CONFIG.update(config)
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
24
|
+
puts opts
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on_tail("--version", "Show version") do
|
29
|
+
puts(version || VERSION)
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
opts.parse!(argv)
|
34
|
+
[argv, opts, options]
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_password(prompt, input=$stdin, output=$stdout)
|
38
|
+
output.print prompt
|
39
|
+
system "/bin/stty -echo" if input.tty?
|
40
|
+
input.gets.chomp
|
41
|
+
ensure
|
42
|
+
system "/bin/stty echo" if input.tty?
|
43
|
+
output.puts
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
module ActiveLdap
|
3
|
+
# Configuration
|
4
|
+
#
|
5
|
+
# Configuration provides the default settings required for
|
6
|
+
# ActiveLdap to work with your LDAP server. All of these
|
7
|
+
# settings can be passed in at initialization time.
|
8
|
+
module Configuration
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
DEFAULT_CONFIG = {}
|
14
|
+
DEFAULT_CONFIG[:host] = '127.0.0.1'
|
15
|
+
DEFAULT_CONFIG[:port] = 389
|
16
|
+
DEFAULT_CONFIG[:method] = :plain # :ssl, :tls, :plain allowed
|
17
|
+
|
18
|
+
DEFAULT_CONFIG[:bind_dn] = "cn=admin,dc=localdomain"
|
19
|
+
DEFAULT_CONFIG[:password_block] = nil
|
20
|
+
DEFAULT_CONFIG[:password] = nil
|
21
|
+
DEFAULT_CONFIG[:store_password] = true
|
22
|
+
DEFAULT_CONFIG[:allow_anonymous] = true
|
23
|
+
DEFAULT_CONFIG[:sasl_quiet] = false
|
24
|
+
DEFAULT_CONFIG[:try_sasl] = false
|
25
|
+
|
26
|
+
DEFAULT_CONFIG[:retry_limit] = 3
|
27
|
+
DEFAULT_CONFIG[:retry_wait] = 3
|
28
|
+
DEFAULT_CONFIG[:timeout] = 0 # in seconds; 0 <= Never timeout
|
29
|
+
# Whether or not to retry on timeouts
|
30
|
+
DEFAULT_CONFIG[:retry_on_timeout] = true
|
31
|
+
|
32
|
+
DEFAULT_CONFIG[:logger] = nil
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
@@defined_configurations = {}
|
36
|
+
|
37
|
+
def default_configuration
|
38
|
+
DEFAULT_CONFIG.dup
|
39
|
+
end
|
40
|
+
|
41
|
+
def ensure_configuration(config=nil)
|
42
|
+
if config.nil?
|
43
|
+
if defined?(LDAP_ENV)
|
44
|
+
config = LDAP_ENV
|
45
|
+
elsif defined?(RAILS_ENV)
|
46
|
+
config = RAILS_ENV
|
47
|
+
else
|
48
|
+
config = {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if config.is_a?(Symbol) or config.is_a?(String)
|
53
|
+
_config = configurations[config.to_s]
|
54
|
+
unless _config
|
55
|
+
raise ConnectionError, "#{config} connection is not configured"
|
56
|
+
end
|
57
|
+
config = _config
|
58
|
+
end
|
59
|
+
|
60
|
+
config
|
61
|
+
end
|
62
|
+
|
63
|
+
def configuration(key=nil)
|
64
|
+
@@defined_configurations[key || active_connection_name]
|
65
|
+
end
|
66
|
+
|
67
|
+
def define_configuration(key, config)
|
68
|
+
@@defined_configurations[key] = config
|
69
|
+
end
|
70
|
+
|
71
|
+
def defined_configurations
|
72
|
+
@@defined_configurations
|
73
|
+
end
|
74
|
+
|
75
|
+
def remove_configuration_by_configuration(config)
|
76
|
+
@@defined_configurations.delete_if {|key, value| value == config}
|
77
|
+
end
|
78
|
+
|
79
|
+
def merge_configuration(config)
|
80
|
+
configuration = default_configuration
|
81
|
+
config.symbolize_keys.each do |key, value|
|
82
|
+
case key
|
83
|
+
when :base
|
84
|
+
# Scrub before inserting
|
85
|
+
self.base = value.gsub(/['}{#]/, '')
|
86
|
+
when :ldap_scope
|
87
|
+
self.ldap_scope = value
|
88
|
+
else
|
89
|
+
configuration[key] = value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
configuration
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module ActiveLdap
|
2
|
+
module Connection
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
@@active_connections = {}
|
9
|
+
|
10
|
+
def active_connections
|
11
|
+
@@active_connections[Thread.current.object_id] ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def active_connection_name
|
15
|
+
@active_connection_name ||= determine_active_connection_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def clear_active_connections!
|
19
|
+
connections = active_connections
|
20
|
+
connections.each do |key, connection|
|
21
|
+
connection.disconnect!
|
22
|
+
end
|
23
|
+
connections.clear
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear_active_connection_name
|
27
|
+
@active_connection_name = nil
|
28
|
+
ObjectSpace.each_object(Class) do |klass|
|
29
|
+
if klass < self and !klass.name.empty?
|
30
|
+
klass.instance_variable_set("@active_connection_name", nil)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def connection
|
36
|
+
conn = nil
|
37
|
+
@active_connection_name ||= nil
|
38
|
+
if @active_connection_name
|
39
|
+
conn = active_connections[@active_connection_name]
|
40
|
+
end
|
41
|
+
unless conn
|
42
|
+
conn = retrieve_connection
|
43
|
+
active_connections[@active_connection_name] = conn
|
44
|
+
end
|
45
|
+
conn
|
46
|
+
end
|
47
|
+
|
48
|
+
def connection=(adaptor)
|
49
|
+
if adaptor.is_a?(Adaptor::Base)
|
50
|
+
@schema = nil
|
51
|
+
active_connections[active_connection_name] = adaptor
|
52
|
+
elsif adaptor.is_a?(Hash)
|
53
|
+
config = adaptor
|
54
|
+
adaptor = Inflector.camelize(config[:adaptor] || "ldap")
|
55
|
+
self.connection = Adaptor.const_get(adaptor).new(config)
|
56
|
+
elsif adaptor.nil?
|
57
|
+
raise ConnectionNotEstablished
|
58
|
+
else
|
59
|
+
establish_connection(adaptor)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def connected?
|
64
|
+
active_connections[active_connection_name] ? true : false
|
65
|
+
end
|
66
|
+
|
67
|
+
def retrieve_connection
|
68
|
+
conn = nil
|
69
|
+
name = active_connection_name
|
70
|
+
raise ConnectionNotEstablished unless name
|
71
|
+
conn = active_connections[name]
|
72
|
+
if conn.nil?
|
73
|
+
config = configuration(name)
|
74
|
+
raise ConnectionNotEstablished unless config
|
75
|
+
self.connection = config
|
76
|
+
conn = active_connections[name]
|
77
|
+
end
|
78
|
+
raise ConnectionNotEstablished if conn.nil?
|
79
|
+
conn
|
80
|
+
end
|
81
|
+
|
82
|
+
def remove_connection(klass=self)
|
83
|
+
key = active_connection_key(klass)
|
84
|
+
config = configuration(key)
|
85
|
+
conn = active_connections[key]
|
86
|
+
remove_configuration_by_configuration(config)
|
87
|
+
active_connections.delete_if {|key, value| value == conn}
|
88
|
+
conn.disconnect! if conn
|
89
|
+
config
|
90
|
+
end
|
91
|
+
|
92
|
+
def establish_connection(config=nil)
|
93
|
+
config = ensure_configuration(config)
|
94
|
+
remove_connection
|
95
|
+
|
96
|
+
clear_active_connection_name
|
97
|
+
key = active_connection_key
|
98
|
+
@active_connection_name = key
|
99
|
+
define_configuration(key, merge_configuration(config))
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return the schema object
|
103
|
+
def schema
|
104
|
+
@schema ||= connection.schema
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def active_connection_key(k=self)
|
109
|
+
k.name.empty? ? k.object_id : k.name
|
110
|
+
end
|
111
|
+
|
112
|
+
def determine_active_connection_name
|
113
|
+
key = active_connection_key
|
114
|
+
if active_connections[key] or configuration(key)
|
115
|
+
key
|
116
|
+
elsif self == ActiveLdap::Base
|
117
|
+
nil
|
118
|
+
else
|
119
|
+
superclass.active_connection_name
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def connection
|
125
|
+
self.class.connection
|
126
|
+
end
|
127
|
+
|
128
|
+
# schema
|
129
|
+
#
|
130
|
+
# Returns the value of self.class.schema
|
131
|
+
# This is just syntactic sugar
|
132
|
+
def schema
|
133
|
+
logger.debug {"stub: called schema"}
|
134
|
+
self.class.schema
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ActiveLdap
|
2
|
+
module ObjectClass
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_class(*target_classes)
|
11
|
+
replace_class((classes + target_classes.flatten).uniq)
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove_class(*target_classes)
|
15
|
+
new_classes = (classes - target_classes.flatten).uniq
|
16
|
+
assert_object_classes(new_classes)
|
17
|
+
set_attribute('objectClass', new_classes)
|
18
|
+
end
|
19
|
+
|
20
|
+
def replace_class(*target_classes)
|
21
|
+
new_classes = target_classes.flatten.uniq
|
22
|
+
assert_object_classes(new_classes)
|
23
|
+
set_attribute('objectClass', new_classes)
|
24
|
+
end
|
25
|
+
|
26
|
+
def classes
|
27
|
+
(get_attribute('objectClass', true) || []).dup
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def assert_object_classes(new_classes)
|
32
|
+
assert_valid_object_class_value_type(new_classes)
|
33
|
+
assert_valid_object_class_value(new_classes)
|
34
|
+
assert_have_all_required_classes(new_classes)
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_valid_object_class_value_type(new_classes)
|
38
|
+
invalid_classes = new_classes.reject do |new_class|
|
39
|
+
new_class.is_a?(String)
|
40
|
+
end
|
41
|
+
unless invalid_classes.empty?
|
42
|
+
message = "Value in objectClass array is not a String"
|
43
|
+
invalid_classes_info = invalid_classes.collect do |invalid_class|
|
44
|
+
"#{invalid_class.class}:#{invalid_class.inspect}"
|
45
|
+
end.join(", ")
|
46
|
+
raise TypeError, "#{message}: #{invalid_classes_info}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def assert_valid_object_class_value(new_classes)
|
51
|
+
invalid_classes = new_classes.reject do |new_class|
|
52
|
+
schema.exist_name?("objectClasses", new_class)
|
53
|
+
end
|
54
|
+
unless invalid_classes.empty?
|
55
|
+
message = "unknown objectClass to LDAP server"
|
56
|
+
message = "#{message}: #{invalid_classes.join(', ')}"
|
57
|
+
raise ObjectClassError, message
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def assert_have_all_required_classes(new_classes)
|
62
|
+
required_classes = self.class.required_classes - new_classes
|
63
|
+
unless required_classes.empty?
|
64
|
+
raise RequiredObjectClassMissed,
|
65
|
+
"Can't remove required objectClass: " +
|
66
|
+
required_classes.join(", ")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
module ActiveLdap
|
2
|
+
class Schema
|
3
|
+
def initialize(entries)
|
4
|
+
@entries = entries
|
5
|
+
@schema_info = {}
|
6
|
+
@class_attributes_info = {}
|
7
|
+
@cache = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def names(group)
|
11
|
+
alias_map(group).keys
|
12
|
+
end
|
13
|
+
|
14
|
+
def exist_name?(group, name)
|
15
|
+
alias_map(group).has_key?(normalize_schema_name(name))
|
16
|
+
end
|
17
|
+
|
18
|
+
# attribute
|
19
|
+
#
|
20
|
+
# This is just like LDAP::Schema#attribute except that it allows
|
21
|
+
# look up in any of the given keys.
|
22
|
+
# e.g.
|
23
|
+
# attribute('attributeTypes', 'cn', 'DESC')
|
24
|
+
# attribute('ldapSyntaxes', '1.3.6.1.4.1.1466.115.121.1.5', 'DESC')
|
25
|
+
def attribute(group, id_or_name, attribute_name)
|
26
|
+
return [] if attribute_name.empty?
|
27
|
+
attribute_name = normalize_attribute_name(attribute_name)
|
28
|
+
value = attributes(group, id_or_name)[attribute_name]
|
29
|
+
value ? value.dup : []
|
30
|
+
end
|
31
|
+
alias_method :[], :attribute
|
32
|
+
alias_method :attr, :attribute
|
33
|
+
|
34
|
+
def attributes(group, id_or_name)
|
35
|
+
return {} if group.empty? or id_or_name.empty?
|
36
|
+
|
37
|
+
# Initialize anything that is required
|
38
|
+
info, ids, aliases = ensure_schema_info(group)
|
39
|
+
id, name = determine_id_or_name(id_or_name, aliases)
|
40
|
+
|
41
|
+
# Check already parsed options first
|
42
|
+
return ids[id] if ids.has_key?(id)
|
43
|
+
|
44
|
+
while schema = @entries[group].shift
|
45
|
+
next unless /\A\s*\(\s*([\d\.]+)\s*(.*)\s*\)\s*\z/ =~ schema
|
46
|
+
schema_id = $1
|
47
|
+
rest = $2
|
48
|
+
next if ids.has_key?(schema_id)
|
49
|
+
|
50
|
+
attributes = {}
|
51
|
+
ids[schema_id] = attributes
|
52
|
+
|
53
|
+
parse_attributes(rest, attributes)
|
54
|
+
(attributes["NAME"] || []).each do |v|
|
55
|
+
normalized_name = normalize_schema_name(v)
|
56
|
+
aliases[normalized_name] = schema_id
|
57
|
+
id = schema_id if id.nil? and name == normalized_name
|
58
|
+
end
|
59
|
+
|
60
|
+
break if id == schema_id
|
61
|
+
end
|
62
|
+
|
63
|
+
ids[id || aliases[name]] || {}
|
64
|
+
end
|
65
|
+
|
66
|
+
# attribute_aliases
|
67
|
+
#
|
68
|
+
# Returns all names from the LDAP schema for the
|
69
|
+
# attribute given.
|
70
|
+
def attribute_aliases(name)
|
71
|
+
cache([:attribute_aliases, name]) do
|
72
|
+
attribute_type(name, 'NAME')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# read_only?
|
77
|
+
#
|
78
|
+
# Returns true if an attribute is read-only
|
79
|
+
# NO-USER-MODIFICATION
|
80
|
+
def read_only?(name)
|
81
|
+
cache([:read_only?, name]) do
|
82
|
+
attribute_type(name, 'NO-USER-MODIFICATION')[0] == 'TRUE'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# single_value?
|
87
|
+
#
|
88
|
+
# Returns true if an attribute can only have one
|
89
|
+
# value defined
|
90
|
+
# SINGLE-VALUE
|
91
|
+
def single_value?(name)
|
92
|
+
cache([:single_value?, name]) do
|
93
|
+
attribute_type(name, 'SINGLE-VALUE')[0] == 'TRUE'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# binary?
|
98
|
+
#
|
99
|
+
# Returns true if the given attribute's syntax
|
100
|
+
# is X-NOT-HUMAN-READABLE or X-BINARY-TRANSFER-REQUIRED
|
101
|
+
def binary?(name)
|
102
|
+
cache([:binary?, name]) do
|
103
|
+
# Get syntax OID
|
104
|
+
syntax = attribute_type(name, 'SYNTAX')[0]
|
105
|
+
!syntax.nil? and
|
106
|
+
(ldap_syntax(syntax, 'X-NOT-HUMAN-READABLE') == ["TRUE"] or
|
107
|
+
ldap_syntax(syntax, 'X-BINARY-TRANSFER-REQUIRED') == ["TRUE"])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# binary_required?
|
112
|
+
#
|
113
|
+
# Returns true if the value MUST be transferred in binary
|
114
|
+
def binary_required?(name)
|
115
|
+
cache([:binary_required?, name]) do
|
116
|
+
# Get syntax OID
|
117
|
+
syntax = attribute_type(name, 'SYNTAX')[0]
|
118
|
+
!syntax.nil? and
|
119
|
+
ldap_syntax(syntax, 'X-BINARY-TRANSFER-REQUIRED') == ["TRUE"]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# class_attributes
|
124
|
+
#
|
125
|
+
# Returns an Array of all the valid attributes (but not with full aliases)
|
126
|
+
# for the given objectClass
|
127
|
+
def class_attributes(objc)
|
128
|
+
cache([:class_attributes, objc]) do
|
129
|
+
# First get all the current level attributes
|
130
|
+
must = object_class(objc, 'MUST')
|
131
|
+
may = object_class(objc, 'MAY')
|
132
|
+
|
133
|
+
# Now add all attributes from the parent object (SUPerclasses)
|
134
|
+
# Hopefully an iterative approach will be pretty speedy
|
135
|
+
# 1. build complete list of SUPs
|
136
|
+
# 2. Add attributes from each
|
137
|
+
sups = object_class(objc, 'SUP')
|
138
|
+
loop do
|
139
|
+
start_size = sups.size
|
140
|
+
new_sups = []
|
141
|
+
sups.each do |sup|
|
142
|
+
new_sups.concat(object_class(sup, 'SUP'))
|
143
|
+
end
|
144
|
+
|
145
|
+
sups.concat(new_sups)
|
146
|
+
sups.uniq!
|
147
|
+
break if sups.size == start_size
|
148
|
+
end
|
149
|
+
sups.each do |sup|
|
150
|
+
must.concat(object_class(sup, 'MUST'))
|
151
|
+
may.concat(object_class(sup, 'MAY'))
|
152
|
+
end
|
153
|
+
|
154
|
+
# Clean out the dupes.
|
155
|
+
must.uniq!
|
156
|
+
may.uniq!
|
157
|
+
if objc == "inetOrgPerson"
|
158
|
+
may.collect! do |name|
|
159
|
+
if name == "x500uniqueIdentifier"
|
160
|
+
"x500UniqueIdentifier"
|
161
|
+
else
|
162
|
+
name
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
{:must => must, :may => may}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
def cache(key)
|
173
|
+
(@cache[key] ||= [yield])[0]
|
174
|
+
end
|
175
|
+
|
176
|
+
def ensure_schema_info(group)
|
177
|
+
@schema_info[group] ||= {:ids => {}, :aliases => {}}
|
178
|
+
info = @schema_info[group]
|
179
|
+
[info, info[:ids], info[:aliases]]
|
180
|
+
end
|
181
|
+
|
182
|
+
def determine_id_or_name(id_or_name, aliases)
|
183
|
+
if /\A[\d\.]+\z/ =~ id_or_name
|
184
|
+
id = id_or_name
|
185
|
+
name = nil
|
186
|
+
else
|
187
|
+
name = normalize_schema_name(id_or_name)
|
188
|
+
id = aliases[name]
|
189
|
+
end
|
190
|
+
[id, name]
|
191
|
+
end
|
192
|
+
|
193
|
+
def parse_attributes(str, attributes)
|
194
|
+
str.scan(/([A-Z\-]+)\s+
|
195
|
+
(?:\(\s*([\w\-]+(?:\s+\$\s+[\w\-]+)+)\s*\)|
|
196
|
+
\(\s*([^\)]*)\s*\)|
|
197
|
+
'([^\']*)'|
|
198
|
+
([a-z][\w\-]*)|
|
199
|
+
(\d[\d\.\{\}]+)|
|
200
|
+
()
|
201
|
+
)/x
|
202
|
+
) do |name, multi_amp, multi, string, literal, syntax, no_value|
|
203
|
+
case
|
204
|
+
when multi_amp
|
205
|
+
values = multi_amp.rstrip.split(/\s*\$\s*/)
|
206
|
+
when multi
|
207
|
+
values = multi.scan(/\s*'([^\']*)'\s*/).collect {|value| value[0]}
|
208
|
+
when string
|
209
|
+
values = [string]
|
210
|
+
when literal
|
211
|
+
values = [literal]
|
212
|
+
when syntax
|
213
|
+
values = [syntax]
|
214
|
+
when no_value
|
215
|
+
values = ["TRUE"]
|
216
|
+
end
|
217
|
+
attributes[name] = values
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def attribute_type(name, attribute_name)
|
222
|
+
cache([:attribute_type, name, attribute_name]) do
|
223
|
+
attribute("attributeTypes", name, attribute_name)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def ldap_syntax(name, attribute_name)
|
228
|
+
cache([:ldap_syntax, name, attribute_name]) do
|
229
|
+
attribute("ldapSyntaxes", name, attribute_name)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def object_class(name, attribute_name)
|
234
|
+
cache([:object_class, name, attribute_name]) do
|
235
|
+
attribute("objectClasses", name, attribute_name)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def alias_map(group)
|
240
|
+
ensure_parse(group)
|
241
|
+
@schema_info[group][:aliases]
|
242
|
+
end
|
243
|
+
|
244
|
+
def ensure_parse(group)
|
245
|
+
unless @entries[group].empty?
|
246
|
+
attribute(group, 'nonexistent', 'nonexistent')
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def normalize_schema_name(name)
|
251
|
+
name.downcase.sub(/;.*$/, '')
|
252
|
+
end
|
253
|
+
|
254
|
+
def normalize_attribute_name(name)
|
255
|
+
name.upcase
|
256
|
+
end
|
257
|
+
end # Schema
|
258
|
+
end
|