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.
Files changed (64) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +12 -0
  3. data/README.markdown +36 -0
  4. data/Rakefile +19 -0
  5. data/ad-framework.gemspec +25 -0
  6. data/doc/open_ldap_server.markdown +17 -0
  7. data/extras/adtest.schema +304 -0
  8. data/extras/slapd.conf +10 -0
  9. data/lib/ad-framework.rb +53 -0
  10. data/lib/ad-framework/attribute.rb +35 -0
  11. data/lib/ad-framework/attribute_type.rb +133 -0
  12. data/lib/ad-framework/auxiliary_class.rb +24 -0
  13. data/lib/ad-framework/config.rb +72 -0
  14. data/lib/ad-framework/config/attribute_definition.rb +18 -0
  15. data/lib/ad-framework/config/mapping.rb +26 -0
  16. data/lib/ad-framework/exceptions.rb +17 -0
  17. data/lib/ad-framework/fields.rb +44 -0
  18. data/lib/ad-framework/patterns/callbacks.rb +47 -0
  19. data/lib/ad-framework/patterns/has_schema.rb +127 -0
  20. data/lib/ad-framework/patterns/persistence.rb +67 -0
  21. data/lib/ad-framework/patterns/searchable.rb +117 -0
  22. data/lib/ad-framework/patterns/validations.rb +50 -0
  23. data/lib/ad-framework/schema.rb +118 -0
  24. data/lib/ad-framework/structural_class.rb +61 -0
  25. data/lib/ad-framework/utilities/entry_builder.rb +77 -0
  26. data/lib/ad-framework/utilities/transaction.rb +32 -0
  27. data/lib/ad-framework/utilities/validator.rb +26 -0
  28. data/lib/ad-framework/version.rb +5 -0
  29. data/test/helper.rb +71 -0
  30. data/test/integration/defined_array_test.rb +49 -0
  31. data/test/integration/defined_integer_test.rb +48 -0
  32. data/test/integration/defined_string_test.rb +48 -0
  33. data/test/integration/defined_top_test.rb +101 -0
  34. data/test/integration/defined_user_test.rb +140 -0
  35. data/test/irb.rb +2 -0
  36. data/test/support/factory.rb +67 -0
  37. data/test/support/ldap.yml +6 -0
  38. data/test/support/schema/attribute_types.rb +67 -0
  39. data/test/support/schema/attributes.rb +10 -0
  40. data/test/support/schema/auxiliary_classes.rb +12 -0
  41. data/test/support/schema/structural_classes.rb +46 -0
  42. data/test/support/seed.rb +28 -0
  43. data/test/support/state.rb +29 -0
  44. data/test/unit/ad-framework/attribute_test.rb +84 -0
  45. data/test/unit/ad-framework/attribute_type/class_methods_test.rb +146 -0
  46. data/test/unit/ad-framework/attribute_type_test.rb +114 -0
  47. data/test/unit/ad-framework/auxiliary_class_test.rb +39 -0
  48. data/test/unit/ad-framework/config/attribute_definition_test.rb +26 -0
  49. data/test/unit/ad-framework/config/mapping_test.rb +41 -0
  50. data/test/unit/ad-framework/config_test.rb +121 -0
  51. data/test/unit/ad-framework/fields_test.rb +44 -0
  52. data/test/unit/ad-framework/patterns/callbacks_test.rb +90 -0
  53. data/test/unit/ad-framework/patterns/has_schema/class_methods_test.rb +214 -0
  54. data/test/unit/ad-framework/patterns/has_schema_test.rb +96 -0
  55. data/test/unit/ad-framework/patterns/persistence_test.rb +126 -0
  56. data/test/unit/ad-framework/patterns/searchable_test.rb +201 -0
  57. data/test/unit/ad-framework/patterns/validations_test.rb +113 -0
  58. data/test/unit/ad-framework/schema_test.rb +268 -0
  59. data/test/unit/ad-framework/structural_class_test.rb +64 -0
  60. data/test/unit/ad-framework/utilities/entry_builder_test.rb +107 -0
  61. data/test/unit/ad-framework/utilities/transaction_test.rb +50 -0
  62. data/test/unit/ad-framework/utilities/validator_test.rb +46 -0
  63. data/test/unit/ad-framework_test.rb +116 -0
  64. 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