ad-framework 0.1.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/.gitignore +6 -0
- data/Gemfile +12 -0
- data/README.markdown +36 -0
- data/Rakefile +19 -0
- data/ad-framework.gemspec +25 -0
- data/doc/open_ldap_server.markdown +17 -0
- data/extras/adtest.schema +304 -0
- data/extras/slapd.conf +10 -0
- data/lib/ad-framework.rb +53 -0
- data/lib/ad-framework/attribute.rb +35 -0
- data/lib/ad-framework/attribute_type.rb +133 -0
- data/lib/ad-framework/auxiliary_class.rb +24 -0
- data/lib/ad-framework/config.rb +72 -0
- data/lib/ad-framework/config/attribute_definition.rb +18 -0
- data/lib/ad-framework/config/mapping.rb +26 -0
- data/lib/ad-framework/exceptions.rb +17 -0
- data/lib/ad-framework/fields.rb +44 -0
- data/lib/ad-framework/patterns/callbacks.rb +47 -0
- data/lib/ad-framework/patterns/has_schema.rb +127 -0
- data/lib/ad-framework/patterns/persistence.rb +67 -0
- data/lib/ad-framework/patterns/searchable.rb +117 -0
- data/lib/ad-framework/patterns/validations.rb +50 -0
- data/lib/ad-framework/schema.rb +118 -0
- data/lib/ad-framework/structural_class.rb +61 -0
- data/lib/ad-framework/utilities/entry_builder.rb +77 -0
- data/lib/ad-framework/utilities/transaction.rb +32 -0
- data/lib/ad-framework/utilities/validator.rb +26 -0
- data/lib/ad-framework/version.rb +5 -0
- data/test/helper.rb +71 -0
- data/test/integration/defined_array_test.rb +49 -0
- data/test/integration/defined_integer_test.rb +48 -0
- data/test/integration/defined_string_test.rb +48 -0
- data/test/integration/defined_top_test.rb +101 -0
- data/test/integration/defined_user_test.rb +140 -0
- data/test/irb.rb +2 -0
- data/test/support/factory.rb +67 -0
- data/test/support/ldap.yml +6 -0
- data/test/support/schema/attribute_types.rb +67 -0
- data/test/support/schema/attributes.rb +10 -0
- data/test/support/schema/auxiliary_classes.rb +12 -0
- data/test/support/schema/structural_classes.rb +46 -0
- data/test/support/seed.rb +28 -0
- data/test/support/state.rb +29 -0
- data/test/unit/ad-framework/attribute_test.rb +84 -0
- data/test/unit/ad-framework/attribute_type/class_methods_test.rb +146 -0
- data/test/unit/ad-framework/attribute_type_test.rb +114 -0
- data/test/unit/ad-framework/auxiliary_class_test.rb +39 -0
- data/test/unit/ad-framework/config/attribute_definition_test.rb +26 -0
- data/test/unit/ad-framework/config/mapping_test.rb +41 -0
- data/test/unit/ad-framework/config_test.rb +121 -0
- data/test/unit/ad-framework/fields_test.rb +44 -0
- data/test/unit/ad-framework/patterns/callbacks_test.rb +90 -0
- data/test/unit/ad-framework/patterns/has_schema/class_methods_test.rb +214 -0
- data/test/unit/ad-framework/patterns/has_schema_test.rb +96 -0
- data/test/unit/ad-framework/patterns/persistence_test.rb +126 -0
- data/test/unit/ad-framework/patterns/searchable_test.rb +201 -0
- data/test/unit/ad-framework/patterns/validations_test.rb +113 -0
- data/test/unit/ad-framework/schema_test.rb +268 -0
- data/test/unit/ad-framework/structural_class_test.rb +64 -0
- data/test/unit/ad-framework/utilities/entry_builder_test.rb +107 -0
- data/test/unit/ad-framework/utilities/transaction_test.rb +50 -0
- data/test/unit/ad-framework/utilities/validator_test.rb +46 -0
- data/test/unit/ad-framework_test.rb +116 -0
- metadata +225 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
module AD
|
2
|
+
module Framework
|
3
|
+
module Patterns
|
4
|
+
|
5
|
+
module Persistence
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def included(klass)
|
9
|
+
klass.class_eval do
|
10
|
+
extend AD::Framework::Patterns::Persistence::ClassMethods
|
11
|
+
include AD::Framework::Patterns::Persistence::InstanceMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
|
19
|
+
def new_entry?
|
20
|
+
!(self.fields[:distinguishedname] || self.fields[:dn])
|
21
|
+
end
|
22
|
+
|
23
|
+
def save
|
24
|
+
if self.new_entry?
|
25
|
+
self.create
|
26
|
+
else
|
27
|
+
self.update
|
28
|
+
end
|
29
|
+
end
|
30
|
+
def create
|
31
|
+
self.fields[:distinguishedname] = self.dn
|
32
|
+
self.fields[:objectclass] = (self.schema.object_classes.collect do |object_class|
|
33
|
+
object_class.schema.ldap_name
|
34
|
+
end).compact
|
35
|
+
self.connection.add({ :dn => self.dn, :attributes => self.fields.to_hash })
|
36
|
+
self.reload
|
37
|
+
end
|
38
|
+
def update
|
39
|
+
self.connection.open do |c|
|
40
|
+
self.fields.changes.each do |name, value|
|
41
|
+
c.replace_attribute(self.dn, name, value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
self.reload
|
45
|
+
end
|
46
|
+
|
47
|
+
def destroy
|
48
|
+
self.connection.delete(self.dn)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
|
55
|
+
def create(args = {})
|
56
|
+
entry = self.new(args)
|
57
|
+
entry.create
|
58
|
+
entry
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'ad-framework/utilities/entry_builder'
|
2
|
+
require 'ad-framework/exceptions'
|
3
|
+
|
4
|
+
module AD
|
5
|
+
module Framework
|
6
|
+
module Patterns
|
7
|
+
|
8
|
+
module Searchable
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def included(klass)
|
12
|
+
klass.class_eval do
|
13
|
+
extend AD::Framework::Patterns::Searchable::ClassMethods
|
14
|
+
include AD::Framework::Patterns::Searchable::InstanceMethods
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
module InstanceMethods
|
21
|
+
|
22
|
+
def reload
|
23
|
+
args = {
|
24
|
+
:where => { :dn__eq => (self.fields[:distinguishedname] || self.fields[:dn]) },
|
25
|
+
:limit => 1
|
26
|
+
}
|
27
|
+
search_args = self.class.build_ad_search_args(args)
|
28
|
+
ldap_entry = self.connection.search(search_args).first
|
29
|
+
if ldap_entry
|
30
|
+
AD::Framework::Utilities::EntryBuilder.new(ldap_entry, { :reload => self })
|
31
|
+
else
|
32
|
+
dn = args[:where][:dn__eq]
|
33
|
+
raise(*[
|
34
|
+
AD::Framework::EntryNotFound,
|
35
|
+
"An entry could not be found with dn #{dn.inspect} (#{self.class})"
|
36
|
+
])
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
module ClassMethods
|
44
|
+
|
45
|
+
def find(dn)
|
46
|
+
dn = self.build_ad_dn(dn)
|
47
|
+
args = { :where => { :dn__eq => dn }, :size => 1 }
|
48
|
+
object = self.fetch_ad_entry(args)
|
49
|
+
if !object
|
50
|
+
dn = args[:where][:dn__eq]
|
51
|
+
raise(*[
|
52
|
+
AD::Framework::EntryNotFound,
|
53
|
+
"An entry could not be found with dn #{dn.inspect} (#{self.class})"
|
54
|
+
])
|
55
|
+
end
|
56
|
+
object
|
57
|
+
end
|
58
|
+
|
59
|
+
def first(args = {})
|
60
|
+
args = { :where => args, :size => 1 }
|
61
|
+
self.fetch_ad_entry(args)
|
62
|
+
end
|
63
|
+
|
64
|
+
def all(args = {})
|
65
|
+
self.fetch_ad_entry(args, true)
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_ad_search_args(args = {})
|
69
|
+
default_args = {
|
70
|
+
:objectclass__eq => self.schema.ldap_name,
|
71
|
+
:base => self.schema.treebase
|
72
|
+
}
|
73
|
+
(args || {}).inject(default_args) do |search_args, (key, value)|
|
74
|
+
case(key.to_sym)
|
75
|
+
when :where
|
76
|
+
if value.kind_of?(Array)
|
77
|
+
value = value.inject({}){|where, condition| where.merge(condition) }
|
78
|
+
end
|
79
|
+
search_args.merge(value)
|
80
|
+
when :limit
|
81
|
+
search_args.merge({ :size => value })
|
82
|
+
else
|
83
|
+
search_args.merge({ key => value })
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
def fetch_ad_entry(args, collection = false)
|
91
|
+
search_args = self.build_ad_search_args(args)
|
92
|
+
results = self.connection.search(search_args)
|
93
|
+
if !collection
|
94
|
+
ldap_entry = results.first
|
95
|
+
AD::Framework::Utilities::EntryBuilder.new(ldap_entry).entry
|
96
|
+
else
|
97
|
+
results.collect do |ldap_entry|
|
98
|
+
AD::Framework::Utilities::EntryBuilder.new(ldap_entry).entry
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_ad_dn(dn)
|
104
|
+
if dn !~ /DC=|CN=/
|
105
|
+
[ "CN=#{dn}", self.treebase ].compact.join(", ")
|
106
|
+
else
|
107
|
+
dn
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'ad-framework/utilities/validator'
|
2
|
+
|
3
|
+
module AD
|
4
|
+
module Framework
|
5
|
+
module Patterns
|
6
|
+
|
7
|
+
module Validations
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def included(klass)
|
11
|
+
klass.class_eval do
|
12
|
+
include AD::Framework::Patterns::Validations::InstanceMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
attr_accessor :errors
|
20
|
+
|
21
|
+
def errors
|
22
|
+
@errors ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
[ :create, :update ].each do |name|
|
26
|
+
|
27
|
+
define_method(name) do
|
28
|
+
if self.valid?
|
29
|
+
super
|
30
|
+
true
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid?
|
39
|
+
validator = AD::Framework::Utilities::Validator.new(self)
|
40
|
+
self.errors = validator.errors
|
41
|
+
self.errors.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'ad-framework/attribute'
|
2
|
+
|
3
|
+
module AD
|
4
|
+
module Framework
|
5
|
+
|
6
|
+
class Schema
|
7
|
+
attr_accessor :ldap_name, :rdn, :attributes, :structural_classes, :auxiliary_classes
|
8
|
+
attr_accessor :mandatory, :klass, :callbacks
|
9
|
+
|
10
|
+
def initialize(klass)
|
11
|
+
self.klass = klass
|
12
|
+
|
13
|
+
self.rdn = :name
|
14
|
+
self.auxiliary_classes = []
|
15
|
+
self.callbacks = {}
|
16
|
+
|
17
|
+
if self.klass.is_a?(::Class) && self.klass.superclass.respond_to?(:schema)
|
18
|
+
self.inherit_from(self.klass.superclass.schema)
|
19
|
+
else
|
20
|
+
self.attributes = Set.new
|
21
|
+
self.mandatory = Set.new
|
22
|
+
self.structural_classes = []
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def ldap_name
|
27
|
+
if @ldap_name
|
28
|
+
[ AD::Framework.config.ldap_prefix,
|
29
|
+
@ldap_name
|
30
|
+
].compact.join
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def treebase
|
35
|
+
if @treebase && self.default_treebase && !(@treebase.include?(self.default_treebase))
|
36
|
+
[ @treebase, self.default_treebase ].join(", ")
|
37
|
+
else
|
38
|
+
(@treebase || self.default_treebase)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def treebase=(new_value)
|
43
|
+
@treebase = new_value
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_attributes(attribute_names)
|
47
|
+
self.add_read_attributes(attribute_names)
|
48
|
+
self.add_write_attributes(attribute_names)
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_read_attributes(attribute_names)
|
52
|
+
attribute_names.collect(&:to_sym).each do |name|
|
53
|
+
AD::Framework::Attribute.new(name).define_reader(self.klass)
|
54
|
+
self.attributes << name.to_sym
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_write_attributes(attribute_names)
|
59
|
+
attribute_names.collect(&:to_sym).each do |name|
|
60
|
+
AD::Framework::Attribute.new(name).define_writer(self.klass)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_mandatory(attribute_names)
|
65
|
+
attribute_names.each do |attribute_name|
|
66
|
+
self.mandatory << attribute_name
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def object_classes
|
71
|
+
[ self.structural_classes.to_a,
|
72
|
+
self.klass,
|
73
|
+
self.auxiliary_classes.to_a
|
74
|
+
].flatten.compact
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_auxiliary_class(klass)
|
78
|
+
self.auxiliary_classes << klass
|
79
|
+
self.bring_in(klass.schema)
|
80
|
+
self.auxiliary_classes.uniq!
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_callback(timing, action, method_names)
|
84
|
+
self.callbacks[action.to_sym] ||= {}
|
85
|
+
self.callbacks[action.to_sym][timing.to_sym] ||= []
|
86
|
+
method_names.each do |method_name|
|
87
|
+
self.callbacks[action.to_sym][timing.to_sym] << method_name
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def inspect
|
92
|
+
attrs_display = [ :klass, :ldap_name, :rdn, :attributes ].collect do |attr|
|
93
|
+
"#{attr}: #{self.send(attr).inspect}"
|
94
|
+
end
|
95
|
+
[ "#<#{self.class} ", attrs_display.join(", "), ">" ].join
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def default_treebase
|
101
|
+
AD::Framework.config.treebase
|
102
|
+
end
|
103
|
+
|
104
|
+
def inherit_from(schema)
|
105
|
+
self.attributes = schema.attributes.dup
|
106
|
+
self.structural_classes = schema.structural_classes.dup
|
107
|
+
self.structural_classes << self.klass.superclass
|
108
|
+
self.mandatory = schema.mandatory.dup
|
109
|
+
end
|
110
|
+
|
111
|
+
def bring_in(schema)
|
112
|
+
self.attributes.merge(schema.attributes)
|
113
|
+
self.mandatory.merge(schema.mandatory)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'ad-framework/fields'
|
2
|
+
require 'ad-framework/patterns/callbacks'
|
3
|
+
require 'ad-framework/patterns/has_schema'
|
4
|
+
require 'ad-framework/patterns/persistence'
|
5
|
+
require 'ad-framework/patterns/searchable'
|
6
|
+
require 'ad-framework/patterns/validations'
|
7
|
+
|
8
|
+
module AD
|
9
|
+
module Framework
|
10
|
+
|
11
|
+
class StructuralClass
|
12
|
+
include AD::Framework::Patterns::HasSchema
|
13
|
+
include AD::Framework::Patterns::Persistence
|
14
|
+
include AD::Framework::Patterns::Searchable
|
15
|
+
include AD::Framework::Patterns::Callbacks
|
16
|
+
include AD::Framework::Patterns::Validations
|
17
|
+
|
18
|
+
attr_accessor :meta_class, :fields
|
19
|
+
|
20
|
+
def initialize(attributes = {})
|
21
|
+
self.meta_class = class << self; self; end
|
22
|
+
|
23
|
+
self.fields = AD::Framework::Fields.new(attributes.delete(:fields) || {})
|
24
|
+
if (treebase = (attributes.delete(:treebase) || attributes.delete("treebase")))
|
25
|
+
self.treebase = treebase
|
26
|
+
end
|
27
|
+
|
28
|
+
self.attributes = attributes
|
29
|
+
end
|
30
|
+
|
31
|
+
def treebase
|
32
|
+
self.schema.treebase
|
33
|
+
end
|
34
|
+
def treebase=(new_value)
|
35
|
+
self.schema.treebase = new_value
|
36
|
+
end
|
37
|
+
|
38
|
+
def connection
|
39
|
+
self.class.connection
|
40
|
+
end
|
41
|
+
|
42
|
+
def inspect
|
43
|
+
(attr_display = self.attributes.collect do |(name, value)|
|
44
|
+
"#{name}: #{value.inspect}"
|
45
|
+
end)
|
46
|
+
attr_display << "treebase: #{self.treebase.inspect}"
|
47
|
+
[ "#<#{self.class} ", attr_display.sort.join(", "), ">" ].join
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
|
52
|
+
def connection
|
53
|
+
AD::Framework.connection
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'ad-framework/auxiliary_class'
|
2
|
+
require 'ad-framework/exceptions'
|
3
|
+
require 'ad-framework/fields'
|
4
|
+
require 'ad-framework/structural_class'
|
5
|
+
|
6
|
+
module AD
|
7
|
+
module Framework
|
8
|
+
module Utilities
|
9
|
+
|
10
|
+
class EntryBuilder
|
11
|
+
attr_accessor :ldap_entry, :entry, :fields
|
12
|
+
|
13
|
+
def initialize(ldap_entry, options = {})
|
14
|
+
self.ldap_entry = ldap_entry
|
15
|
+
|
16
|
+
if self.ldap_entry
|
17
|
+
self.fields = AD::Framework::Fields.new(self.ldap_entry || {})
|
18
|
+
if options[:reload]
|
19
|
+
self.entry = options[:reload]
|
20
|
+
self.reload
|
21
|
+
else
|
22
|
+
self.build
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def reload
|
28
|
+
self.entry.fields = self.fields
|
29
|
+
self.entry.schema.attributes.each do |name|
|
30
|
+
self.entry.send("#{name}_attribute_type").reset
|
31
|
+
end
|
32
|
+
self.link_auxiliary_classes
|
33
|
+
end
|
34
|
+
|
35
|
+
def build
|
36
|
+
structure = self.classes_structure
|
37
|
+
self.entry = structure[:structural_class].new({ :fields => self.fields })
|
38
|
+
self.link_auxiliary_classes(structure)
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def link_auxiliary_classes(structure = self.classes_structure)
|
44
|
+
(structure[:auxiliary_classes] || []).each do |klass|
|
45
|
+
if !self.entry.schema.auxiliary_classes.include?(klass)
|
46
|
+
self.entry.meta_class.class_eval do
|
47
|
+
include klass
|
48
|
+
end
|
49
|
+
end
|
50
|
+
self.entry.schema.add_auxiliary_class(klass)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def classes_structure
|
55
|
+
(self.fields["objectclass"] || []).inject({}) do |processed, ldap_name|
|
56
|
+
object_class = AD::Framework.defined_object_classes[ldap_name]
|
57
|
+
if !object_class
|
58
|
+
raise(*[
|
59
|
+
AD::Framework::ObjectClassNotDefined,
|
60
|
+
"An object class with the name #{ldap_name.inspect} is not defined"
|
61
|
+
])
|
62
|
+
end
|
63
|
+
if object_class.ancestors.include?(AD::Framework::StructuralClass)
|
64
|
+
processed[:structural_class] = object_class
|
65
|
+
elsif object_class.included_modules.include?(AD::Framework::AuxiliaryClass)
|
66
|
+
processed[:auxiliary_classes] ||= []
|
67
|
+
processed[:auxiliary_classes] << object_class
|
68
|
+
end
|
69
|
+
processed
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|