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,201 @@
1
+ require 'ldap'
2
+ require 'slf4r'
3
+ require 'ldap/conditions_2_filter'
4
+
5
+ module Ldap
6
+ class Connection < LDAP::Conn
7
+
8
+ attr_reader :base, :host, :port
9
+
10
+ def initialize(config)
11
+ super(config[:host], config[:port])
12
+ @base = config[:base]
13
+ @port = config[:port]
14
+ @host = config[:host]
15
+ set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
16
+ end
17
+
18
+ end
19
+
20
+ class RubyLdapFacade
21
+
22
+ # @param config Hash for the ldap connection
23
+ def self.open(config)
24
+ ldap2 = Connection.new(config)
25
+ ldap2.bind(config[:auth][:username], config[:auth][:password]) do |ldap|
26
+ yield ldap
27
+ end
28
+ end
29
+
30
+ include ::Slf4r::Logger
31
+
32
+ # @param config Hash for the ldap connection
33
+ def initialize(config)
34
+ if config.is_a? Hash
35
+ @ldap2 = Connection.new(config)
36
+ @ldap2.bind(config[:auth][:username], config[:auth][:password])
37
+ else
38
+ @ldap2 = config
39
+ end
40
+ end
41
+
42
+ def retrieve_next_id(treebase, key_field)
43
+ max = 0
44
+ @ldap2.search("#{treebase},#{@ldap2.base}",
45
+ LDAP::LDAP_SCOPE_SUBTREE,
46
+ "(objectclass=*)",
47
+ [key_field]) do |entry|
48
+ n = (entry.vals(key_field) || [0]).first.to_i
49
+ max = n if max < n
50
+ end
51
+ max + 1
52
+ end
53
+
54
+ # @param dn_prefix String the prefix of the dn
55
+ # @param treebase the treebase of the dn or any search
56
+ # @param key_field field which carries the integer unique id of the entity
57
+ # @param props Hash of the ldap attributes of the new ldap object
58
+ # @return nil in case of an error or the new id of the created object
59
+ def create_object(dn_prefix, treebase, key_field, props, silence = false)
60
+ base = "#{treebase},#{@ldap2.base}"
61
+ mods = props.collect do |k,v|
62
+ LDAP.mod(LDAP::LDAP_MOD_ADD, k.to_s, v.is_a?(::Array) ? v : [v.to_s] )
63
+ end
64
+ if @ldap2.add( dn(dn_prefix, treebase), mods)
65
+ props[key_field.to_sym]
66
+ else
67
+ unless silence
68
+ msg = ldap_error("create",
69
+ dn(dn_prefix, treebase)) + "\n\t#{props.inspect}"
70
+ # TODO maybe raise always an error
71
+ if @ldap2.get_operation_result.code.to_s == "68"
72
+ raise ::DataMapper::PersistenceError.new(msg)
73
+ else
74
+ logger.warn(msg)
75
+ end
76
+ end
77
+ nil
78
+ end
79
+ end
80
+
81
+ # @param treebase the treebase of the search
82
+ # @param key_fields Array of fields which carries the integer unique id(s) of the entity
83
+ # @param Array of conditions for the search
84
+ # @return Array of Hashes with a name/values pair for each attribute
85
+ def read_objects(treebase, key_fields, conditions, field_names, order_field = '')
86
+
87
+ if !conditions.nil? and conditions.size > 0
88
+ filter = Conditions2Filter.convert(conditions).to_s
89
+ else
90
+ filter = "(objectclass=*)"
91
+ end
92
+
93
+ result = []
94
+ begin
95
+ @ldap2.search("#{treebase},#{@ldap2.base}",
96
+ LDAP::LDAP_SCOPE_SUBTREE,
97
+ filter,
98
+ field_names, false, 0, 0, order_field) do |res|
99
+ mapp = to_map(field_names, res)
100
+ # TODO maybe make filter which removes this unless
101
+ # TODO move this into the ldap_Adapter to make it more general, so that
102
+ # all field with Integer gets converted, etc
103
+ # NOTE: somehow the fields are downcase coming from query.model
104
+ result << mapp if key_fields.detect do |key_field|
105
+ mapp.keys.detect {|k| k.to_s.downcase == key_field.downcase }
106
+ end
107
+ end
108
+ end
109
+ result
110
+ end
111
+
112
+
113
+ # @param dn_prefix String the prefix of the dn
114
+ # @param treebase the treebase of the dn or any search
115
+ # @param actions the add/replace/delete actions on the attributes
116
+ # @return nil in case of an error or true
117
+ def update_object(dn_prefix, treebase, actions)
118
+ mods = actions.collect do |act|
119
+ mod_op = case act[0]
120
+ when :add
121
+ LDAP::LDAP_MOD_ADD
122
+ when :replace
123
+ LDAP::LDAP_MOD_REPLACE
124
+ when :delete
125
+ LDAP::LDAP_MOD_DELETE
126
+ end
127
+ LDAP.mod(mod_op, act[1].to_s, act[2] == [] ? [] : [act[2].to_s])
128
+ end
129
+ if @ldap2.modify( dn(dn_prefix, treebase),
130
+ mods )
131
+ true
132
+ else
133
+ logger.warn(ldap_error("update",
134
+ dn(dn_prefix, treebase) + "\n\t#{actions.inspect}"))
135
+ nil
136
+ end
137
+ end
138
+
139
+ # @param dn_prefix String the prefix of the dn
140
+ # @param treebase the treebase of the dn or any search
141
+ # @return nil in case of an error or true
142
+ def delete_object(dn_prefix, treebase)
143
+ if @ldap2.delete( dn(dn_prefix, treebase) )
144
+ true
145
+ else
146
+ logger.warn(ldap_error("delete",
147
+ dn(dn_prefix, treebase)))
148
+
149
+ nil
150
+ end
151
+ end
152
+
153
+
154
+ # @param dn String for identifying the ldap object
155
+ # @param password String to be used for authenticate to the dn
156
+ def authenticate(dn, password)
157
+ bound = false
158
+ ldap_con = LDAP::Conn.new(@ldap2.host, @ldap2.port)
159
+ ldap_con.set_option( LDAP::LDAP_OPT_PROTOCOL_VERSION, 3 )
160
+ begin
161
+ ldap_con.bind(dn, password, LDAP::LDAP_AUTH_SIMPLE) do
162
+ bound = true
163
+ end
164
+ rescue LDAP::ResultError => msg
165
+ if msg.to_s =~ /Invalid\ credentials/i
166
+ logger.info("Invalid Credentials: #{dn}")
167
+ else
168
+ logger.warn "Authentication Error: #{msg.to_s}"
169
+ end
170
+ end
171
+ bound
172
+ end
173
+
174
+ # helper to concat the dn from the various parts
175
+ # @param dn_prefix String the prefix of the dn
176
+ # @param treebase the treebase of the dn or any search
177
+ # @return the complete dn String
178
+ def dn(dn_prefix, treebase)
179
+ "#{dn_prefix},#{treebase},#{@ldap2.base}"
180
+ end
181
+
182
+ private
183
+
184
+ # helper to extract the Hash from the ldap search result
185
+ # @param Entry from the ldap_search
186
+ # @return Hash with name/value pairs of the entry
187
+ def to_map(field_names, entry)
188
+ fields = {:dn => :dn}
189
+ field_names.each { |f| fields[f.downcase.to_sym] = f.to_sym }
190
+ map = {}
191
+ LDAP::entry2hash(entry).each do |k,v|
192
+ map[fields[k.downcase.to_sym]] = v
193
+ end
194
+ map
195
+ end
196
+
197
+ def ldap_error(method, dn)
198
+ "#{method} error: (#{@ldap2.get_operation_result.code}) #{@ldap2.get_operation_result.message}\n\tDN: #{dn}"
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,2 @@
1
+ require 'dm-transactions'
2
+ require 'adapters/noop_transaction'
@@ -0,0 +1,188 @@
1
+ require 'ldap'
2
+ require 'slf4r'
3
+ require 'ldap/conditions_2_filter'
4
+
5
+ module Ldap
6
+ # class Connection < LDAP::Conn
7
+
8
+ # attr_reader :base, :host, :port
9
+
10
+ # def initialize(config)
11
+ # super(config[:host], config[:port])
12
+ # @base = config[:base]
13
+ # @port = config[:port]
14
+ # @host = config[:host]
15
+ # set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
16
+ # end
17
+
18
+ # end
19
+
20
+ class RubyLdapFacade
21
+
22
+ # @param config Hash for the ldap connection
23
+ def self.open(config)
24
+ ldap3 = com.unboundid.ldap.sdk.LDAPConnection.new(config[:host], config[:port], config[:base] + "." + config[:auth][:username], config[:auth][:password])
25
+
26
+ yield ldap
27
+ end
28
+
29
+ include ::Slf4r::Logger
30
+
31
+ # @param config Hash for the ldap connection
32
+ def initialize(config)
33
+ if config.is_a? Hash
34
+ @ldap2 = Connection.new(config)
35
+ @ldap2.bind(config[:auth][:username], config[:auth][:password])
36
+ else
37
+ @ldap2 = config
38
+ end
39
+ end
40
+
41
+ def retrieve_next_id(treebase, key_field)
42
+ max = 0
43
+ @ldap2.search("#{treebase},#{@ldap2.base}",
44
+ LDAP::LDAP_SCOPE_SUBTREE,
45
+ "(objectclass=*)",
46
+ [key_field]) do |entry|
47
+ n = (entry.vals(key_field) || [0]).first.to_i
48
+ max = n if max < n
49
+ end
50
+ max + 1
51
+ end
52
+
53
+ # @param dn_prefix String the prefix of the dn
54
+ # @param treebase the treebase of the dn or any search
55
+ # @param key_field field which carries the integer unique id of the entity
56
+ # @param props Hash of the ldap attributes of the new ldap object
57
+ # @return nil in case of an error or the new id of the created object
58
+ def create_object(dn_prefix, treebase, key_field, props, silence = false)
59
+ base = "#{treebase},#{@ldap2.base}"
60
+ mods = props.collect do |k,v|
61
+ LDAP.mod(LDAP::LDAP_MOD_ADD, k.to_s, v.is_a?(::Array) ? v : [v.to_s] )
62
+ end
63
+ if @ldap2.add( dn(dn_prefix, treebase), mods)
64
+ # :attributes => props) and @ldap.get_operation_result.code.to_s == "0"
65
+ props[key_field.downcase.to_sym]
66
+ else
67
+ unless silence
68
+ msg = ldap_error("create",
69
+ dn(dn_prefix, treebase)) + "\n\t#{props.inspect}"
70
+ # TODO maybe raise always an error
71
+ if @ldap2.get_operation_result.code.to_s == "68"
72
+ raise ::DataMapper::PersistenceError.new(msg)
73
+ else
74
+ logger.warn(msg)
75
+ end
76
+ end
77
+ nil
78
+ end
79
+ end
80
+
81
+ # @param treebase the treebase of the search
82
+ # @param key_fields Array of fields which carries the integer unique id(s) of the entity
83
+ # @param Array of conditions for the search
84
+ # @return Array of Hashes with a name/values pair for each attribute
85
+ def read_objects(treebase, key_fields, conditions, field_names, order_field = '')
86
+ filter = Conditions2Filter.convert(conditions)
87
+ result = []
88
+ begin
89
+ @ldap2.search("#{treebase},#{@ldap2.base}",
90
+ LDAP::LDAP_SCOPE_SUBTREE,
91
+ filter.to_s == "" ? "(objectclass=*)" : filter.to_s.gsub(/\(\(/, "(").gsub(/\)\)/, ")"),
92
+ field_names, false, 0, 0, order_field) do |res|
93
+
94
+ map = to_map(res)
95
+ #puts map[key_field.to_sym]
96
+ # TODO maybe make filter which removes this unless
97
+ # TODO move this into the ldap_Adapter to make it more general, so that
98
+ # all field with Integer gets converted, etc
99
+ result << map if key_fields.detect do |key_field|
100
+ map.member? key_field.to_sym
101
+ end
102
+ end
103
+ end
104
+ result
105
+ end
106
+
107
+
108
+ # @param dn_prefix String the prefix of the dn
109
+ # @param treebase the treebase of the dn or any search
110
+ # @param actions the add/replace/delete actions on the attributes
111
+ # @return nil in case of an error or true
112
+ def update_object(dn_prefix, treebase, actions)
113
+ mods = actions.collect do |act|
114
+ mod_op = case act[0]
115
+ when :add
116
+ LDAP::LDAP_MOD_ADD
117
+ when :replace
118
+ LDAP::LDAP_MOD_REPLACE
119
+ when :delete
120
+ LDAP::LDAP_MOD_DELETE
121
+ end
122
+ LDAP.mod(mod_op, act[1].to_s, act[2] == [] ? [] : [act[2].to_s])
123
+ end
124
+ if @ldap2.modify( dn(dn_prefix, treebase),
125
+ mods )
126
+ true
127
+ else
128
+ logger.warn(ldap_error("update",
129
+ dn(dn_prefix, treebase) + "\n\t#{actions.inspect}"))
130
+ nil
131
+ end
132
+ end
133
+
134
+ # @param dn_prefix String the prefix of the dn
135
+ # @param treebase the treebase of the dn or any search
136
+ # @return nil in case of an error or true
137
+ def delete_object(dn_prefix, treebase)
138
+ if @ldap2.delete( dn(dn_prefix, treebase) )
139
+ true
140
+ else
141
+ logger.warn(ldap_error("delete",
142
+ dn(dn_prefix, treebase)))
143
+
144
+ nil
145
+ end
146
+ end
147
+
148
+
149
+ # @param dn String for identifying the ldap object
150
+ # @param password String to be used for authenticate to the dn
151
+ def authenticate(dn, password)
152
+ Net::LDAP.new( { :host => @ldap2.host,
153
+ :port => @ldap2.port,
154
+ :auth => {
155
+ :method => :simple,
156
+ :username => dn,
157
+ :password => password
158
+ },
159
+ :base => @ldap2.base
160
+ } ).bind
161
+ end
162
+
163
+ # helper to concat the dn from the various parts
164
+ # @param dn_prefix String the prefix of the dn
165
+ # @param treebase the treebase of the dn or any search
166
+ # @return the complete dn String
167
+ def dn(dn_prefix, treebase)
168
+ "#{dn_prefix},#{treebase},#{@ldap2.base}"
169
+ end
170
+
171
+ private
172
+
173
+ # helper to extract the Hash from the ldap search result
174
+ # @param Entry from the ldap_search
175
+ # @return Hash with name/value pairs of the entry
176
+ def to_map(entry)
177
+ map = {}
178
+ LDAP::entry2hash(entry).each do |k,v|
179
+ map[k.downcase.to_sym] = v
180
+ end
181
+ map
182
+ end
183
+
184
+ def ldap_error(method, dn)
185
+ "#{method} error: (#{@ldap2.get_operation_result.code}) #{@ldap2.get_operation_result.message}\n\tDN: #{dn}"
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,3 @@
1
+ module Ldap
2
+ VERSION = '0.4.0.alpha1'.freeze
3
+ end
@@ -0,0 +1,189 @@
1
+ require 'ldap/digest'
2
+ require 'dm-core'
3
+ require 'ldap/array'
4
+
5
+ module DataMapper
6
+ module Model
7
+
8
+ def load(records, query)
9
+ repository = query.repository
10
+ repository_name = repository.name
11
+ fields = query.fields
12
+ discriminator = properties(repository_name).discriminator
13
+ no_reload = !query.reload?
14
+
15
+ field_map = {}
16
+ fields.each { |property| field_map[property] = property.field }
17
+
18
+ records.map do |record|
19
+ identity_map = nil
20
+ key_values = nil
21
+ resource = nil
22
+
23
+ case record
24
+ when Hash
25
+ # remap fields to use the Property object
26
+ record = record.dup
27
+ field_map.each { |property, field| record[property] = record.delete(field) if record.key?(field) }
28
+
29
+ model = discriminator && discriminator.load(record[discriminator]) || self
30
+ model_key = model.key(repository_name)
31
+
32
+ resource = if model_key.valid?(key_values = record.values_at(*model_key))
33
+ identity_map = repository.identity_map(model)
34
+ identity_map[key_values]
35
+ end
36
+
37
+ resource ||= model.allocate
38
+
39
+ fields.each do |property|
40
+ next if no_reload && property.loaded?(resource)
41
+
42
+ value = record[property]
43
+
44
+ value = property.load(value)
45
+
46
+ property.set!(resource, value)
47
+ end
48
+
49
+ when Resource
50
+ model = record.model
51
+ model_key = model.key(repository_name)
52
+
53
+ resource = if model_key.valid?(key_values = record.key)
54
+ identity_map = repository.identity_map(model)
55
+ identity_map[key_values]
56
+ end
57
+
58
+ resource ||= model.allocate
59
+
60
+ fields.each do |property|
61
+ next if no_reload && property.loaded?(resource)
62
+
63
+ property.set!(resource, property.get!(record))
64
+ end
65
+ end
66
+
67
+ resource.instance_variable_set(:@_repository, repository)
68
+
69
+ if identity_map
70
+ resource.persisted_state = Resource::State::Clean.new(resource) unless resource.persisted_state?
71
+
72
+ # defer setting the IdentityMap so second level caches can
73
+ # record the state of the resource after loaded
74
+ identity_map[key_values] = resource
75
+ else
76
+ resource.persisted_state = Resource::State::Immutable.new(resource)
77
+ end
78
+
79
+ resource
80
+ end
81
+ end
82
+
83
+ module LdapResource
84
+
85
+ Model.append_inclusions self
86
+
87
+ # authenticate the current resource against the stored password
88
+ # @param [String] password to authenticate
89
+ # @return [TrueClass, FalseClass] whether password was right or wrong
90
+ def authenticate(password)
91
+ ldap.authenticate(ldap.dn(self.class.dn_prefix(self),
92
+ self.class.treebase),
93
+ password)
94
+ end
95
+
96
+ # if called without parameter or block the given properties get returned.
97
+ # if called with a block then the block gets stored. if called with new
98
+ # properties they get stored. if called with a Resource then either the
99
+ # stored block gets called with that Resource or the stored properties get
100
+ # returned.
101
+ # @param [Hash,DataMapper::Resource] properties_or_resource either a Hash with properties, a Resource or nil
102
+ # @param [block] &block to be stored for later calls when properties_or_resource is nil
103
+ # @return [Hash] when called with a Resource
104
+ def ldap_properties(properties_or_resource = nil, &block)
105
+ if properties_or_resource
106
+ if properties_or_resource.instance_of? Hash
107
+ @ldap_properties = properties_or_resource
108
+ elsif @ldap_properties.instance_of? Hash
109
+ @ldap_properties
110
+ else
111
+ @ldap_properties.call(properties_or_resource)
112
+ end
113
+ else
114
+ @ldap_properties = block
115
+ end
116
+ end
117
+
118
+ # if called without parameter or block the given treebase gets returned.
119
+ # if called with a block then the block gets stored. if called with a
120
+ # String then it gets stored. if called with a Resource then either the
121
+ # stored block gets called with that Resource or the stored String gets
122
+ # returned.
123
+ # @param [String,DataMapper::Resource] treebase_or_resource either a String, a Resource or nil
124
+ # @param [block] &block to be stored for later calls when base_or_resource is nil
125
+ # @return [String] when called with a Resource
126
+ def treebase(base_or_resource = nil, &block)
127
+ if base_or_resource
128
+ if base_or_resource.instance_of? String
129
+ @treebase = base_or_resource
130
+ elsif @treebase.instance_of? String
131
+ @treebase
132
+ else
133
+ @treebase.call(base_or_resource)
134
+ end
135
+ else
136
+ if block
137
+ @treebase = block
138
+ else # backwards compatibility
139
+ @treebase
140
+ end
141
+ end
142
+ end
143
+
144
+ # if called without parameter or block the given dn_prefix gets returned.
145
+ # if called with a block then the block gets stored. if called with a
146
+ # String then it gets stored. if called with a Resource then either the
147
+ # stored block gets called with that Resource or the stored String gets
148
+ # returned.
149
+ # @param [String,DataMapper::Resource] prefix_or_resource either a String, a Resource or nil
150
+ # @param [&block] block to be stored for later calls
151
+ # @return [String, nil] when called with a Resource
152
+ def dn_prefix(prefix_or_resource = nil, &block)
153
+ if prefix_or_resource
154
+ if prefix_or_resource.instance_of? String
155
+ @ldap_dn = prefix_or_resource
156
+ elsif @ldap_dn.instance_of? String
157
+ @ldap_dn
158
+ else
159
+ @ldap_dn.call(prefix_or_resource)
160
+ end
161
+ else
162
+ @ldap_dn = block
163
+ end
164
+ end
165
+
166
+ # if called without parameter then the stored field gets returned
167
+ # otherwise the given parameters gets stored
168
+ # @param [Symbol, String] field a new multivalue_field
169
+ # @return [Symbol] the multivalue_field
170
+ def multivalue_field(field = nil)
171
+ if field.nil?
172
+ @ldap_multivalue_field
173
+ else
174
+ @ldap_multivalue_field = field.to_sym
175
+ end
176
+ end
177
+
178
+ private
179
+ # short cut to the ldap facade
180
+ # @return [Ldap::LdapFacade]
181
+ def ldap
182
+ raise "not an ldap adapter #{repository.adapter.name}" unless repository.adapter.respond_to? :ldap
183
+ repository.adapter.ldap
184
+ end
185
+ end
186
+
187
+ include LdapResource
188
+ end
189
+ end