dm-ldap-adapter 0.4.3-java

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.
@@ -0,0 +1,35 @@
1
+ require "dm-core"
2
+
3
+ module Ldap
4
+ class NoopTransaction
5
+
6
+ def close ; end
7
+ def begin ; end
8
+ def prepare ; end
9
+ def commit ; end
10
+ def rollback ; end
11
+ def rollback_prepared ; end
12
+
13
+ end
14
+ end
15
+
16
+ module DataMapper
17
+ module Adapters
18
+ class LdapAdapter
19
+ def transaction_primitive
20
+ ::Ldap::NoopTransaction.new
21
+ end
22
+ def push_transaction(transaction)
23
+ @transaction = transaction
24
+ end
25
+
26
+ def pop_transaction
27
+ @transaction
28
+ end
29
+
30
+ def current_transaction
31
+ @transaction
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1 @@
1
+ require 'adapter/ldap-adapter'
@@ -0,0 +1,60 @@
1
+ require 'slf4r/logger'
2
+ require 'ldap/digest'
3
+
4
+ # dummy implementation which turns the extra ldap configuration noops
5
+ module DataMapper
6
+ module Resource
7
+
8
+ module ClassMethods
9
+
10
+ include ::Slf4r::Logger
11
+
12
+ def ldap_properties(resource = nil, &block)
13
+ if block
14
+ @ldap_properties = block
15
+ elsif resource.instance_of? Hash
16
+ @ldap_properties = resource
17
+ logger.debug { "ldap_properties=#{@ldap_properties.inspect}" }
18
+ elsif resource
19
+ logger.debug { "ldap_properties=#{@ldap_properties.call(resource).inspect}" }
20
+ else
21
+ logger.debug { "ldap_properties=#{@ldap_properties.inspect}" }
22
+ end
23
+ end
24
+
25
+ def treebase(resource = nil, &block)
26
+ if block
27
+ @treebase = block
28
+ elsif resource.instance_of? String
29
+ @treebase = resource
30
+ logger.debug { "treebase=#{@treebase.inspect}" }
31
+ elsif resource
32
+ logger.debug { "treebase=#{@treebase.call(resource).inspect}" }
33
+ else
34
+ logger.debug { "treebase=#{@treebase}" }
35
+ end
36
+ end
37
+
38
+ def dn_prefix(resource = nil, &block)
39
+ if block
40
+ @dn_prefix = block
41
+ elsif resource.instance_of? Hash
42
+ @dn_prefix = resource
43
+ logger.debug { "dn_prefix=#{@dn_prefix.inspect}" }
44
+ elsif resource
45
+ logger.debug { "dn_prefix=#{@dn_prefix.call(resource).inspect}" }
46
+ else
47
+ logger.debug { "dn_prefix=#{dn_prefix}" }
48
+ end
49
+ end
50
+
51
+ def multivalue_field(field = nil)
52
+ logger.debug { "multivalue_field = #{field}" } if field
53
+ end
54
+ end
55
+
56
+ def authenticate(password)
57
+ raise "NotImplemented"
58
+ end
59
+ end
60
+ end
data/lib/ldap/array.rb ADDED
@@ -0,0 +1,122 @@
1
+ require 'dm-core'
2
+ module Ldap
3
+ class Array < ::Array
4
+
5
+ def initialize(resource, property, *args)
6
+ setup(resource, property)
7
+ super(args)
8
+ end
9
+
10
+ def setup(resource, property)
11
+ @resource = resource
12
+ @property = property
13
+ self
14
+ end
15
+
16
+ alias :push! :push
17
+
18
+ def []=(k, v)
19
+ ar = [self].flatten
20
+ ar[k] = v
21
+ @resource.send("#{@property.name}=".to_sym, ar)
22
+ super
23
+ end
24
+
25
+ def <<(element)
26
+ push(element)
27
+ end
28
+
29
+ def push(element)
30
+ ar = [self].flatten
31
+ ar.push(element)
32
+ @resource.send("#{@property.name}=".to_sym, ar)
33
+ super
34
+ end
35
+
36
+ alias :delete! :delete
37
+
38
+ def delete(element)
39
+ ar = [self].flatten
40
+ ar.delete(element)
41
+ @resource.send(:"#{@property.name}=", ar)
42
+ super
43
+ end
44
+ end
45
+
46
+ class LdapArray < ::DataMapper::Property::Text
47
+
48
+ default Proc.new { |r,p| Ldap::Array.new(r,p) }
49
+
50
+ def custom?
51
+ true
52
+ end
53
+
54
+ def primitive?(value)
55
+ super || value.kind_of?(::Array)
56
+ end
57
+
58
+ def load(value)
59
+ result = case value
60
+ when ::String then value[1, value.size-2].split('","').to_a.freeze
61
+ when ::Array then value.freeze
62
+ else
63
+ []
64
+ end
65
+ end
66
+
67
+ def dump(value)
68
+ result = case value
69
+ when LdapArray then '"' + value.join('","') + '"'
70
+ when ::Array then '"' + value.join('","') + '"'
71
+ when ::String then '"' + value.to_s + '"'
72
+ else
73
+ nil
74
+ end
75
+ end
76
+
77
+ # keep the *args so it works for both DM-1.1.x and DM-1.0.x
78
+ def initialize(_model = nil, _name = nil, options = {}, *args)
79
+ super
80
+
81
+ add_writer(model,name) unless options[:writer] == :private || options[:accessor] == :private
82
+ add_reader(model,name) unless options[:reader] == :private || options[:accessor] == :private
83
+ end
84
+
85
+ private
86
+
87
+ def add_reader(model, name)
88
+ #Creates instance method for reader
89
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
90
+ def #{name}
91
+ attr_data = attribute_get(:#{name})
92
+
93
+ case attr_data
94
+ when Ldap::Array
95
+ attr_data.setup(self, properties[:#{name}])
96
+ else
97
+ new_ldap_array = Ldap::Array.new(self, properties[:#{name}])
98
+ new_ldap_array.replace(attr_data || [])
99
+ end
100
+ end
101
+ RUBY
102
+ end
103
+
104
+ def add_writer(model, name)
105
+ #Creates instance method for writer
106
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
107
+ def #{name}=(input)
108
+ data = case input
109
+ when Ldap::Array
110
+ input.setup(self, properties[:#{name}])
111
+ else
112
+ new_ldap_array = Ldap::Array.new(self, properties[:#{name}])
113
+ new_ldap_array.replace(input || [])
114
+ end
115
+
116
+ attribute_set(:#{name}, data)
117
+ end
118
+ RUBY
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,95 @@
1
+ require 'net/ldap'
2
+
3
+ module Ldap
4
+ class Conditions2Filter
5
+
6
+ @@logger = ::Slf4r::LoggerFacade.new(::Ldap::Conditions2Filter)
7
+
8
+ # @param Array of conditions for the search
9
+ # @return Array of Hashes with a name/values pair for each attribute
10
+ def self.convert(conditions)
11
+ @@logger.debug { "conditions #{conditions.inspect}" }
12
+ filters = []
13
+ conditions.each do |cond|
14
+ c = cond[2]
15
+ case cond[0]
16
+ when :or_operator
17
+ f = nil
18
+ cond[1].each do |cc|
19
+ ff = case cc[0]
20
+ when :eql
21
+ Net::LDAP::Filter.eq( cc[1].to_s, cc[2].to_s )
22
+ when :gte
23
+ Net::LDAP::Filter.ge( cc[1].to_s, cc[2].to_s )
24
+ when :lte
25
+ Net::LDAP::Filter.le( cc[1].to_s, cc[2].to_s )
26
+ when :like
27
+ Net::LDAP::Filter.eq( cc[1].to_s, cc[2].to_s.gsub(/%/, "*").gsub(/_/, "*").gsub(/\*\*/, "*") )
28
+ else
29
+ logger.error(cc[0].to_s + " needs coding")
30
+ end
31
+ if f
32
+ f = f | ff
33
+ else
34
+ f = ff
35
+ end
36
+ end
37
+ when :eql
38
+ if c.nil?
39
+ f = ~ Net::LDAP::Filter.pres( cond[1].to_s )
40
+ elsif c.respond_to? :each
41
+ f = nil
42
+ c.each do |cc|
43
+ if f
44
+ f = f | Net::LDAP::Filter.eq( cond[1].to_s, cc.to_s )
45
+ else
46
+ f = Net::LDAP::Filter.eq( cond[1].to_s, cc.to_s )
47
+ end
48
+ end
49
+ #elsif c.class == Range
50
+ # p c
51
+ # f = Net::LDAP::Filter.ge( cond[1].to_s, c.begin.to_s ) & Net::LDAP::Filter.le( cond[1].to_s, c.end.to_s )
52
+ else
53
+ f = Net::LDAP::Filter.eq( cond[1].to_s, c.to_s )
54
+ end
55
+ when :gte
56
+ f = Net::LDAP::Filter.ge( cond[1].to_s, c.to_s )
57
+ when :lte
58
+ f = Net::LDAP::Filter.le( cond[1].to_s, c.to_s )
59
+ when :not
60
+ if c.nil?
61
+ f = Net::LDAP::Filter.pres( cond[1].to_s )
62
+ elsif c.respond_to? :each
63
+ f = nil
64
+ c.each do |cc|
65
+ if f
66
+ f = f | Net::LDAP::Filter.eq( cond[1].to_s, cc.to_s )
67
+ else
68
+ f = Net::LDAP::Filter.eq( cond[1].to_s, cc.to_s )
69
+ end
70
+ end
71
+ f = ~ f
72
+ else
73
+ f = ~ Net::LDAP::Filter.eq( cond[1].to_s, c.to_s )
74
+ end
75
+ when :like
76
+ f = Net::LDAP::Filter.eq( cond[1].to_s, c.to_s.gsub(/%/, "*").gsub(/_/, "*").gsub(/\*\*/, "*") )
77
+ else
78
+ logger.error(cond[0].to_s + " needs coding")
79
+ end
80
+ filters << f if f
81
+ end
82
+
83
+ filter = nil
84
+ filters.each do |f|
85
+ if filter.nil?
86
+ filter = f
87
+ else
88
+ filter = filter & f
89
+ end
90
+ end
91
+ @@logger.debug { "search filter: (#{filter.to_s})" }
92
+ filter
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,30 @@
1
+ begin
2
+ require 'sha1'
3
+ rescue LoadError
4
+ # ruby1.9.x
5
+ require 'digest/sha1'
6
+ SHA1 = Digest::SHA1
7
+ end
8
+
9
+ require 'base64'
10
+ module Ldap
11
+ class Digest
12
+ # method from openldap faq which produces the userPassword attribute
13
+ # for the ldap
14
+ # @param secret String the password
15
+ # @param salt String the salt for the password digester
16
+ # @return the encoded password/salt
17
+ def self.ssha(secret, salt)
18
+ (salt.empty? ? "{SHA}": "{SSHA}") +
19
+ Base64.encode64(::Digest::SHA1.digest(secret + salt) + salt).gsub(/\n/, '')
20
+ end
21
+
22
+ # method from openldap faq which produces the userPassword attribute
23
+ # for the ldap
24
+ # @param secret String the password
25
+ # @return the encoded password
26
+ def self.sha(secret)
27
+ ssha(secret, "")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,161 @@
1
+ require 'net/ldap'
2
+ require 'ldap/conditions_2_filter'
3
+
4
+ module Ldap
5
+ class NetLdapFacade
6
+
7
+ # @param config Hash for the ldap connection
8
+ def self.open(config)
9
+ Net::LDAP.open( config ) do |ldap|
10
+ yield ldap
11
+ end
12
+ end
13
+
14
+ include ::Slf4r::Logger
15
+
16
+ # @param config Hash for the ldap connection
17
+ def initialize(config)
18
+ if config.is_a? Hash
19
+ @ldap = Net::LDAP.new( config )
20
+ else
21
+ @ldap = config
22
+ end
23
+ end
24
+
25
+ def retrieve_next_id(treebase, key_field)
26
+ base = "#{treebase},#{@ldap.base}"
27
+ id_sym = key_field.downcase.to_sym
28
+ max = 0
29
+ @ldap.search( :base => base,
30
+ :attributes => [key_field],
31
+ :return_result => false ) do |entry|
32
+ n = entry[id_sym].first.to_i
33
+ max = n if max < n
34
+ end
35
+ max + 1
36
+ end
37
+
38
+ # @param dn_prefix String the prefix of the dn
39
+ # @param treebase the treebase of the dn or any search
40
+ # @param key_field field which carries the integer unique id of the entity
41
+ # @param props Hash of the ldap attributes of the new ldap object
42
+ # @return nil in case of an error or the new id of the created object
43
+ def create_object(dn_prefix, treebase, key_field, props, silence = false)
44
+ base = "#{treebase},#{@ldap.base}"
45
+ if @ldap.add( :dn => dn(dn_prefix, treebase),
46
+ :attributes => props) || @ldap.get_operation_result.code.to_s == "0"
47
+ props[key_field.to_sym]
48
+ else
49
+ unless silence
50
+ msg = ldap_error("create",
51
+ dn(dn_prefix, treebase)) + "\n\t#{props.inspect}"
52
+ # TODO maybe raise always an error
53
+ if @ldap.get_operation_result.code.to_s == "68"
54
+ raise ::DataMapper::PersistenceError.new(msg)
55
+ else
56
+ logger.warn(msg)
57
+ end
58
+ end
59
+ nil
60
+ end
61
+ end
62
+
63
+ # @param treebase the treebase of the search
64
+ # @param key_fields Array of fields which carries the integer unique id(s) of the entity
65
+ # @param Array of conditions for the search
66
+ # @return Array of Hashes with a name/values pair for each attribute
67
+ def read_objects(treebase, key_fields, conditions, field_names, order_field = nil)
68
+ result = []
69
+ filter = Conditions2Filter.convert(conditions)
70
+ @ldap.search( :base => "#{treebase},#{@ldap.base}",
71
+ :attributes => field_names,
72
+ :filter => filter ) do |res|
73
+ mapp = to_map(field_names, res)
74
+
75
+ #puts map[key_field.to_sym]
76
+ # TODO maybe make filter which removes this unless
77
+ # TODO move this into the ldap_Adapter to make it more general, so that
78
+ # all field with Integer gets converted, etc
79
+ result << mapp if key_fields.detect do |key_field|
80
+ mapp.keys.detect {|k| k.to_s.downcase == key_field.downcase }
81
+ end
82
+ end
83
+ result
84
+ end
85
+
86
+
87
+ # @param dn_prefix String the prefix of the dn
88
+ # @param treebase the treebase of the dn or any search
89
+ # @param actions the add/replace/delete actions on the attributes
90
+ # @return nil in case of an error or true
91
+ def update_object(dn_prefix, treebase, actions)
92
+ if @ldap.modify( :dn => dn(dn_prefix, treebase),
93
+ :operations => actions ) || @ldap.get_operation_result.code.to_s == "0"
94
+ true
95
+ else
96
+ logger.warn(ldap_error("update",
97
+ dn(dn_prefix, treebase) + "\n\t#{actions.inspect}"))
98
+ nil
99
+ end
100
+ end
101
+
102
+ # @param dn_prefix String the prefix of the dn
103
+ # @param treebase the treebase of the dn or any search
104
+ # @return nil in case of an error or true
105
+ def delete_object(dn_prefix, treebase)
106
+ if @ldap.delete( :dn => dn(dn_prefix, treebase) )
107
+ true
108
+ else
109
+ logger.warn(ldap_error("delete",
110
+ dn(dn_prefix, treebase)))
111
+
112
+ nil
113
+ end
114
+ end
115
+
116
+
117
+ # @param dn String for identifying the ldap object
118
+ # @param password String to be used for authenticate to the dn
119
+ def authenticate(dn, password)
120
+ Net::LDAP.new( { :host => @ldap.host,
121
+ :port => @ldap.port,
122
+ :auth => {
123
+ :method => :simple,
124
+ :username => dn,
125
+ :password => password
126
+ },
127
+ :base => @ldap.base
128
+ } ).bind
129
+ end
130
+
131
+ # helper to concat the dn from the various parts
132
+ # @param dn_prefix String the prefix of the dn
133
+ # @param treebase the treebase of the dn or any search
134
+ # @return the complete dn String
135
+ def dn(dn_prefix, treebase)
136
+ "#{dn_prefix},#{treebase},#{@ldap.base}"
137
+ end
138
+
139
+ private
140
+
141
+ # helper to extract the Hash from the ldap search result
142
+ # @param Entry from the ldap_search
143
+ # @return Hash with name/value pairs of the entry
144
+ def to_map(field_names, entry)
145
+ fields = {:dn => :dn}
146
+ field_names.each { |f| fields[f.downcase.to_sym] = f.to_sym }
147
+ def entry.map
148
+ @myhash
149
+ end
150
+ result = {}
151
+ entry.map.each do |k,v|
152
+ result[fields[k]] = v
153
+ end
154
+ result
155
+ end
156
+
157
+ def ldap_error(method, dn)
158
+ "#{method} error: (#{@ldap.get_operation_result.code}) #{@ldap.get_operation_result.message}\n\tDN: #{dn}"
159
+ end
160
+ end
161
+ end