Class: LdapModel
- Inherits:
-
Object
- Object
- LdapModel
- Defined in:
- lib/ldapmodel/search.rb,
lib/ldapmodel/helpers.rb,
lib/ldapmodel/profiler.rb,
lib/ldapmodel/attr_conv.rb,
lib/ldapmodel/connection.rb,
lib/ldapmodel/class_store.rb
Overview
Connection management
Direct Known Subclasses
PuavoRest::BootServer, PuavoRest::ExternalFile, PuavoRest::ExternalService, PuavoRest::Group, PuavoRest::Host, PuavoRest::LegacyRole, PuavoRest::Organisation, PuavoRest::PrinterQueue, PuavoRest::SambaDomain, PuavoRest::SambaGroup, PuavoRest::School, PuavoRest::User
Defined Under Namespace
Classes: LdapHashError
Constant Summary
- ESCAPES =
http://tools.ietf.org/html/rfc4515 lists these exceptions from UTF1 charset for filters. All of the following must be escaped in any normal string using a single backslash ('\') as escape.
{ "\0" => '00', # NUL = %x00 ; null character '*' => '2A', # ASTERISK = %x2A ; asterisk ("*") '(' => '28', # LPARENS = %x28 ; left parenthesis ("(") ')' => '29', # RPARENS = %x29 ; right parenthesis (")") '\\' => '5C', # ESC = %x5C ; esc (or backslash) ("\") }
- ESCAPE_RE =
Compiled character class regexp using the keys from the above hash.
Regexp.new( "[" + ESCAPES.keys.map { |e| Regexp.escape(e) }.join + "]" )
- PROF =
LdapSearchProfiler.new "ldapsearch"
- KRB_LOCK =
Mutex.new
- @@_class_store =
Store for ldap attribute mappings
{}
Instance Attribute Summary (collapse)
-
- (Object) ldap_attr_store
readonly
Returns the value of attribute ldap_attr_store.
-
- (Object) serialize_attrs
readonly
Returns the value of attribute serialize_attrs.
Class Method Summary (collapse)
- + (Object) _class_store
-
+ (Object) after(*states, &hook_block) { ... }
Register block to be executed on the given states.
-
+ (Object) all(attrs = nil)
Return all ldap entries from the current base.
-
+ (Object) base_filter
When filtering models with LdapModel.filter this filter will be added to it automatically with AND operator (&).
-
+ (Object) before(*states, &hook_block) { ... }
Register block to be executed on the given states.
-
+ (Array<LdapModel>, LdapModel) by_attr(pretty_name, value, option = nil, attrs = nil)
Find model by it's mapped attribute.
-
+ (Array<LdapModel>, LdapModel) by_attr!(attr, value, option = nil, attrs = nil)
Find model by it's mapped attribute.
-
+ (Object) by_dn(dn, attrs = nil)
Find model by
dn
attribute. -
+ (Object) by_dn!(*args)
Find model by
dn
attribute. -
+ (Object) by_dn_array(dns)
Get array of models by their dn attributes.
-
+ (Object) by_id(id)
Find model by
id
attribute. -
+ (Object) by_id!(id)
Find model by
id
attribute. -
+ (Array<LdapModel>, LdapModel) by_ldap_attr(attr, value, option = nil, attrs = nil)
Filter models by a attribute.
-
+ (Array<LdapModel>, LdapModel) by_ldap_attr!(attr, value, option = nil, attrs = nil)
Filter models by a attribute.
- + (Object) callable_from_instance(method)
-
+ (Object) class_store(name)
Like double at sign attributes (@@foo) but they are not shared between subclasses.
- + (Object) clear_setup
-
+ (Object) computed_attr(attr, serialize_name = nil)
A method that will be executed and added to
to_hash
andto_json
conversions of this models. - + (Object) connection
-
+ (Object) create_connection
Create connection for LdapModel.
-
+ (Proc) create_filter_lambda(pretty_attr, &convert)
Return a lambda which converts ldap field value to ldap search filter.
-
+ (Object) dn_bind(dn, pw)
Do LDAP bind with dn and password.
-
+ (Object) escape(string)
Escape unsafe user input for safe LDAP filter use.
-
+ (Object) filter(filter_, attrs = nil)
Do LDAP search with a filter.
- + (Object) from_ldap_hash(ldap_attrs, serialize_attrs = nil)
-
+ (Object) inherited(subclass)
copy attributes to inherited subclasses.
- + (Object) is_dn(s)
- + (Boolean) is_not_found?(err)
-
+ (Array<Symbol>) ldap_attrs
LDAP attributes that will be converted.
-
+ (Object) ldap_base
Override in a subclass.
-
+ (Object) ldap_map(ldap_name, pretty_name, options = nil) {|value| ... }
Define conversion between LDAP attribute and the JSON attribute.
-
+ (Object) ldap_op(method, *args, &block)
ruby-ldap operation wrapper.
-
+ (Object) organisation
Get configured organisation.
-
+ (Boolean) organisation?
Return true if an organisation is configured.
-
+ (Object) pretty_attrs_to_ldap(attrs = nil)
Convert array of pretty names to ldap attribute names.
-
+ (Object) raw_by_dn(dn, attributes = nil)
Find any ldap entry by dn.
-
+ (Array) raw_filter(base, filter, attributes = nil, &block)
LDAP::LDAP_SCOPE_SUBTREE filter search for #ldap_base.
-
+ (Object) sasl_bind(ticket)
Do LDAP sasl bind with a kerberos ticket.
-
+ (Object) search(keywords)
Search for models using keywords.
- + (Array<Proc>) search_filters
- + (Object) settings
- + (Object) settings=(settings)
-
+ (Object) setup(opts, &block)
Configure LDAP bind for LdapModel.
-
+ (Object) skip_serialize(*attrs)
Skip this attribute(s) from serializations such as
to_hash
andto_json
.
Instance Method Summary (collapse)
- - (Object) [](pretty_name) deprecated Deprecated.
- - (Object) []=(pretty_name, value) deprecated Deprecated.
-
- (Object) add(pretty_name, value)
Append value to LdapConverters::ArrayValue attribute.
-
- (Object) add_validation_error(attr, code, message)
Add validation error.
-
- (Object) as_json
Object.
-
- (Object) assert_validation(message = nil)
Raises ValidationError if #add_validation_error was called at least once.
-
- (Boolean) changed?(pretty_name)
Returns true if this value is going to be written to ldap on next #save! call.
-
- (Object) create!(_dn = nil)
Save new model to LDAP.
-
- (Boolean) dirty?
Returns true when the model has unsaved changes in attributes.
-
- (Boolean) empty?
Returns trur when the model has no values.
- - (Object) get_own(pretty_name)
- - (Object) get_raw(ldap_name)
-
- (LdapModel) initialize(attrs = {}, options = {})
constructor
A new instance of LdapModel.
-
- (Object) ldap_merge!(hash)
Merge hash of ldap attributes to this model.
-
- (Object) ldap_set(ldap_name, value)
Set attribute using the original ldap attribute.
- - (Object) link(path)
-
- (Object) merge(other)
Merge value from other LdapModel to this one.
-
- (Boolean) new?
Returns true if the model is present in LDAP.
- - (Object) object_model
-
- (Object) save!
Save changes to LDAP.
- - (Object) set(pretty_name, value) deprecated Deprecated.
-
- (Object) to_hash
Convert model to Hash.
-
- (Object) to_json
String.
- - (Object) to_ldap_hash
-
- (Object) transform(pretty_name, method, value)
Transform value using the attribute transformer.
- - (Object) update!(h)
-
- (Object) validate
Validation method called before saving.
-
- (Object) validate!(message = nil)
Run hooks and validations.
-
- (Object) validate_unique(pretty_name)
Validate uniqueness of an attribute.
-
- (Object) write_raw(ldap_name, new_val)
Write raw ldap value.
Constructor Details
- (LdapModel) initialize(attrs = {}, options = {})
Returns a new instance of LdapModel
15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/ldapmodel/attr_conv.rb', line 15 def initialize(attrs={}, ={}) @existing = !![:existing] @ldap_attr_store = [:store] || {} if [:serialize] @serialize_attrs = Set.new([:serialize].map{|a| a.to_sym}) end @cache = {} @validation_errors = {} reset_pending update!(attrs) end |
Instance Attribute Details
- (Object) ldap_attr_store (readonly)
Returns the value of attribute ldap_attr_store
12 13 14 |
# File 'lib/ldapmodel/attr_conv.rb', line 12 def ldap_attr_store @ldap_attr_store end |
- (Object) serialize_attrs (readonly)
Returns the value of attribute serialize_attrs
13 14 15 |
# File 'lib/ldapmodel/attr_conv.rb', line 13 def serialize_attrs @serialize_attrs end |
Class Method Details
+ (Object) _class_store
4 5 6 |
# File 'lib/ldapmodel/class_store.rb', line 4 def self._class_store @@_class_store[self] ||= {} end |
+ (Object) after(*states, &hook_block) { ... }
Register block to be executed on the given states
43 44 45 46 47 48 |
# File 'lib/ldapmodel/attr_conv.rb', line 43 def self.after(*states, &hook_block) hooks[:after] ||= {} states.each do |state| (hooks[:after][state.to_sym] ||= []).push(hook_block) end end |
+ (Object) all(attrs = nil)
Return all ldap entries from the current base
151 152 153 |
# File 'lib/ldapmodel/search.rb', line 151 def self.all(attrs=nil) filter(base_filter, attrs) end |
+ (Object) base_filter
When filtering models with filter this filter will be added to it automatically with AND operator (&). Usefull when there are multiple LdapModels is the same LDAP branch / base.
Override this in subclasses when needed.
182 183 184 |
# File 'lib/ldapmodel/search.rb', line 182 def self.base_filter "(objectclass=*)" end |
+ (Object) before(*states, &hook_block) { ... }
Register block to be executed on the given states
35 36 37 38 39 40 |
# File 'lib/ldapmodel/attr_conv.rb', line 35 def self.before(*states, &hook_block) hooks[:before] ||= {} states.each do |state| (hooks[:before][state.to_sym] ||= []).push(hook_block) end end |
+ (Array<LdapModel>, LdapModel) by_attr(pretty_name, value, option = nil, attrs = nil)
Find model by it's mapped attribute. It's safe to call with user input since the value is escaped before ldap search.
114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/ldapmodel/search.rb', line 114 def self.by_attr(pretty_name, value, option=nil, attrs=nil) ldap_attr = pretty2ldap[pretty_name.to_sym] if ldap_attr.nil? # Would compile to invalid ldap search filter. Throw early with human # readable error message raise "Invalid pretty attribute #{ pretty_name } for #{ self }" end by_ldap_attr(ldap_attr, value, option, attrs) end |
+ (Array<LdapModel>, LdapModel) by_attr!(attr, value, option = nil, attrs = nil)
Find model by it's mapped attribute. It's safe to call with user input since the value is escaped before ldap search.
Raises NotFound if no models were found
129 130 131 |
# File 'lib/ldapmodel/search.rb', line 129 def self.by_attr!(attr, value, option=nil, attrs=nil) by_ldap_attr!(pretty2ldap[attr.to_sym], value, option, attrs) end |
+ (Object) by_dn(dn, attrs = nil)
Find model by dn
attribute.
192 193 194 195 |
# File 'lib/ldapmodel/search.rb', line 192 def self.by_dn(dn, attrs=nil) res = raw_by_dn(dn, pretty_attrs_to_ldap(attrs)) from_ldap_hash(res) if res end |
+ (Object) by_dn!(*args)
Find model by dn
attribute.
Raises NotFound if no models were found
200 201 202 203 204 205 206 |
# File 'lib/ldapmodel/search.rb', line 200 def self.by_dn!(*args) res = by_dn(*args) if not res raise NotFound, :user => "Cannot find #{ self.class } by dn: #{ args.first.inspect }" end res end |
+ (Object) by_dn_array(dns)
Get array of models by their dn attributes.
Nonexistent DNs are ignored.
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/ldapmodel/search.rb', line 214 def self.by_dn_array(dns) timer = PROF.start res = Array(dns).map do |dn| begin by_dn(dn) rescue LDAP::ResultError # Ignore broken dn pointers end end.compact timer.stop("#{ self.name }#by_dn_array(<with #{ dns.size } items>)") res end |
+ (Object) by_id(id)
Find model by id
attribute.
137 138 139 |
# File 'lib/ldapmodel/search.rb', line 137 def self.by_id(id) by_attr(:id, id) end |
+ (Object) by_id!(id)
Find model by id
attribute.
Raises NotFound if no models were found.
144 145 146 |
# File 'lib/ldapmodel/search.rb', line 144 def self.by_id!(id) by_attr!(:id, id) end |
+ (Array<LdapModel>, LdapModel) by_ldap_attr(attr, value, option = nil, attrs = nil)
Filter models by a attribute.
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/ldapmodel/search.rb', line 82 def self.by_ldap_attr(attr, value, option=nil, attrs=nil) custom_filter = "(#{ escape attr }=#{ escape value })" full_filter = "(&#{ base_filter }#{ custom_filter })" res = Array(filter(full_filter, attrs)) if option == :multi res else res.first end end |
+ (Array<LdapModel>, LdapModel) by_ldap_attr!(attr, value, option = nil, attrs = nil)
Filter models by a attribute.
Raises NotFound if no models were found
96 97 98 99 100 101 102 103 104 105 |
# File 'lib/ldapmodel/search.rb', line 96 def self.by_ldap_attr!(attr, value, option=nil, attrs=nil) res = by_ldap_attr(attr, value, option, attrs) if Array(res).empty? raise( NotFound, "Cannot find #{ self } by #{ attr }=#{ value }" ) end res end |
+ (Object) callable_from_instance(method)
3 4 5 6 7 8 |
# File 'lib/ldapmodel/helpers.rb', line 3 def self.callable_from_instance(method) klass = self define_method method do |*args| klass.send(method, *args) end end |
+ (Object) class_store(name)
Like double at sign attributes (@@foo) but they are not shared between subclasses
class Foo
class_store :bar
def get_baz
[:baz]
end
end
Foo.[:baz] = 1
assert Foo.new.get_baz == 1
22 23 24 25 26 27 28 29 30 |
# File 'lib/ldapmodel/class_store.rb', line 22 def self.class_store(name) _class_store[name] = {} define_method(name) do self.class._class_store[name] end define_singleton_method(name) do _class_store[name] end end |
+ (Object) clear_setup
152 153 154 |
# File 'lib/ldapmodel/connection.rb', line 152 def self.clear_setup self.settings = nil end |
+ (Object) computed_attr(attr, serialize_name = nil)
A method that will be executed and added to to_hash
and to_json
conversions of this models
135 136 137 |
# File 'lib/ldapmodel/attr_conv.rb', line 135 def self.computed_attr(attr, serialize_name=nil) computed_attributes[attr.to_sym] = serialize_name || attr end |
+ (Object) connection
126 127 128 129 130 131 132 133 |
# File 'lib/ldapmodel/connection.rb', line 126 def self.connection if conn = settings[:credentials_cache][:current_connection] return conn end if settings[:credentials] settings[:credentials_cache][:current_connection] = create_connection end end |
+ (Object) create_connection
Create connection for LdapModel
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/ldapmodel/connection.rb', line 54 def self.create_connection raise "Cannot create connection without credentials" if settings[:credentials].nil? credentials = settings[:credentials] conn = nil if credentials[:kerberos] return sasl_bind(credentials[:kerberos]) end if credentials[:dn].to_s.strip.empty? raise BadCredentials, "DN missing" if not credentials[:dn] end if credentials[:password].to_s.strip.empty? raise BadCredentials, "Password missing" if not credentials[:password] end begin conn = dn_bind(credentials[:dn], credentials[:password]) rescue LDAP::ResultError => err if err. == "Invalid credentials" raise BadCredentials, { :msg => "Invalid credentials (dn/pw)", :meta => { :dn => credentials[:dn], :username => credentials[:username] }} else raise LdapError, "Other LDAP error: #{ err. }" end end if conn.nil? raise LdapError, "ldap bind returned nil instead of connection" end return conn end |
+ (Proc) create_filter_lambda(pretty_attr, &convert)
Return a lambda which converts ldap field value to ldap search filter
Example:
l = create_filter_lambda(:username) { |value| "*#{ v }*" }
filter = l.call("foo")
"(uid=*foo*)"
273 274 275 276 277 278 279 280 281 |
# File 'lib/ldapmodel/search.rb', line 273 def self.create_filter_lambda(pretty_attr, &convert) if convert.nil? convert = lambda { |v| "*#{ v }*" } end ldap_attr = pretty2ldap[pretty_attr.to_sym] raise "Unknown pretty attribute '#{ pretty_attr }' for #{ self }" if not ldap_attr lambda { |keyword| "(#{ ldap_attr }=#{ convert.call(escape(keyword)) })" } end |
+ (Object) dn_bind(dn, pw)
Do LDAP bind with dn and password
45 46 47 48 49 50 51 |
# File 'lib/ldapmodel/connection.rb', line 45 def self.dn_bind(dn, pw) conn = LDAP::Conn.new(CONFIG["ldap"]) conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) conn.start_tls conn.bind(dn, pw) conn end |
+ (Object) escape(string)
Escape unsafe user input for safe LDAP filter use
41 42 43 |
# File 'lib/ldapmodel/helpers.rb', line 41 def self.escape(string) string.to_s.gsub(ESCAPE_RE) { |char| "\\" + ESCAPES[char] } end |
+ (Object) filter(filter_, attrs = nil)
Do LDAP search with a filter. ldap_base will be used as the base.
66 67 68 69 70 71 |
# File 'lib/ldapmodel/search.rb', line 66 def self.filter(filter_, attrs=nil) ldap_attributes = pretty_attrs_to_ldap(attrs) raw_filter(ldap_base, filter_, ldap_attributes).map! do |entry| from_ldap_hash(entry, attrs) end end |
+ (Object) from_ldap_hash(ldap_attrs, serialize_attrs = nil)
10 11 12 |
# File 'lib/ldapmodel/helpers.rb', line 10 def self.from_ldap_hash(ldap_attrs, serialize_attrs=nil) new({}, :serialize => serialize_attrs, :existing => true).ldap_merge!(ldap_attrs) end |
+ (Object) inherited(subclass)
copy attributes to inherited subclasses
34 35 36 37 38 39 |
# File 'lib/ldapmodel/class_store.rb', line 34 def self.inherited(subclass) _class_store.keys.each do |k| subclass._class_store[k] ||= {} subclass._class_store[k].merge!(_class_store[k]) end end |
+ (Object) is_dn(s)
14 15 16 17 18 |
# File 'lib/ldapmodel/helpers.rb', line 14 def self.is_dn(s) # Could be slightly better I think :) # but usernames should have no commas or equal signs s && s.include?(",") && s.include?("=") end |
+ (Boolean) is_not_found?(err)
210 211 212 |
# File 'lib/ldapmodel/connection.rb', line 210 def self.is_not_found?(err) !!(err && err.class == LDAP::ResultError && err. == "No such object") end |
+ (Array<Symbol>) ldap_attrs
Returns LDAP attributes that will be converted
341 342 343 |
# File 'lib/ldapmodel/attr_conv.rb', line 341 def self.ldap_attrs ldap2pretty.keys end |
+ (Object) ldap_base
Override in a subclass
8 9 10 |
# File 'lib/ldapmodel/search.rb', line 8 def self.ldap_base raise "ldap_base is not implemented for #{ self.name }" end |
+ (Object) ldap_map(ldap_name, pretty_name, options = nil) {|value| ... }
Define conversion between LDAP attribute and the JSON attribute. This will
create a getter method named by the pretty_name
param
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/ldapmodel/attr_conv.rb', line 68 def self.ldap_map(ldap_name, pretty_name, =nil, &transform_block) pretty_name = pretty_name.to_sym ldap_name = ldap_name.to_sym pretty2ldap[pretty_name] = ldap_name ldap2pretty[ldap_name] = pretty_name mapping_string = "#{ self }.ldap_map(:#{ ldap_name }, :#{ pretty_name })" if ![NilClass, Class, Hash].include?(.class) raise "#{mapping_string} has invalid options argument: #{ .inspect }" end transform = LdapConverters::SingleValue default_value = nil if .class == Hash transform = [:transform] if [:transform] default_value = [:default] elsif transform = end if transform_block && transform.class != Class raise "#{mapping_string} cannot use both transform instance and transform block" end if transform_block && transform.class == Class # Inherit the transform class and override the read method with the given # block transform = Class.new(transform) transform.send(:define_method, :read, &transform_block) end [pretty_name] = { :default => default_value, :transform => transform } # Create simple getter for the attribute if no custom one is defined if not method_defined?(pretty_name) define_method pretty_name do get_own(pretty_name) end end setter_method = (pretty_name.to_s + "=").to_sym if not method_defined?(setter_method) define_method setter_method do |value| error = transform.new(self).validate(value) if error add_validation_error(pretty_name, error[:code], error[:message]) # Raise type check validation error early here because later it can # cause more weird errors during hooks and validation assert_validation else write_raw(ldap_name, transform.new(self).write(value)) end end end end |
+ (Object) ldap_op(method, *args, &block)
ruby-ldap operation wrapper
The raw ruby-ldap gives very little information on errors. So wrap it and add a lot more details to the error wrapper.
Log start and end of the operation to syslog. It should make it lot easier to see which slapd log messages are related to the ruby-ldap operation. Slapd logs levels must be raised in order the take advantage of this.
Convert LDAP::ResultError: "No such object" errors to nil return values to make it consistent with every other not found error.
Each operation are given an UUID so the user response, puavo-rest log and syslog logs can be combined
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/ldapmodel/connection.rb', line 172 def self.ldap_op(method, *args, &block) res = nil ldap_op_uuid = (0...25).map{('a'..'z').to_a[rand(10)] }.join Syslog.log(Syslog::LOG_NOTICE, "START(#{ ldap_op_uuid })> #{ connection.class }##{ method }") err = nil begin res = connection.send(method, *args, &block) rescue Exception => _err err = _err # not really an error. Just convert to nil response return if is_not_found?(err) = "\n#{ err.class }: #{ err }\n\n was raised by\n\n" += "UUID: #{ ldap_op_uuid }\n" += "#{ connection.class }##{ method }(#{ args.map{|a| a.inspect }.join(", ")})\n" raise LdapError, { :user => "#{ err.class }: #{ err. } (LDAP OP UUID: #{ ldap_op_uuid })", :message => , :op_uuid => ldap_op_uuid, :original_error => err, :args => args, :method => method } ensure end_msg = "OK" if err end_msg = " ERROR: #{ err.class } #{ err. }" end Syslog.log(Syslog::LOG_NOTICE, "END(#{ ldap_op_uuid })> #{ end_msg }") end res end |
+ (Object) organisation
Get configured organisation
144 145 146 147 148 149 150 |
# File 'lib/ldapmodel/connection.rb', line 144 def self.organisation if settings[:organisation].nil? raise BadInput, :user => "Cannot configure organisation for this request" else settings[:organisation] end end |
+ (Boolean) organisation?
Return true if an organisation is configured
138 139 140 |
# File 'lib/ldapmodel/connection.rb', line 138 def self.organisation? !!settings[:organisation] end |
+ (Object) pretty_attrs_to_ldap(attrs = nil)
Convert array of pretty names to ldap attribute names
55 56 57 58 59 60 |
# File 'lib/ldapmodel/search.rb', line 55 def self.pretty_attrs_to_ldap(attrs=nil) if attrs attrs = attrs.map{|a| pretty2ldap[a.to_sym]}.compact end attrs end |
+ (Object) raw_by_dn(dn, attributes = nil)
Find any ldap entry by dn
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/ldapmodel/search.rb', line 159 def self.raw_by_dn(dn, attributes=nil) attributes ||= ldap_attrs.map{ |a| a.to_s } timer = PROF.start if connection.nil? raise "Connection is not setup!" end res = nil raw_filter(dn, "(objectclass=*)", attributes) do |entry| res = entry.to_hash break end res end |
+ (Array) raw_filter(base, filter, attributes = nil, &block)
LDAP::LDAP_SCOPE_SUBTREE filter search for #ldap_base
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/ldapmodel/search.rb', line 19 def self.raw_filter(base, filter, attributes=nil, &block) res = [] attributes ||= ldap_attrs if not connection raise "Cannot search without a connection" end timer = PROF.start if block.nil? block = lambda do |entry| res.push(entry.to_hash) if entry.dn != base end end begin ldap_op( :search, base, LDAP::LDAP_SCOPE_SUBTREE, filter, attributes.map{ |a| a.to_s }, &block ) ensure timer.stop("#{ self.name }#raw_filter(#{ filter.inspect }) base:#{ base } attributes:#{ attributes.inspect } found #{ res.size } items") PROF.count(timer) end res end |
+ (Object) sasl_bind(ticket)
Do LDAP sasl bind with a kerberos ticket
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/ldapmodel/connection.rb', line 12 def self.sasl_bind(ticket) conn = LDAP::Conn.new(CONFIG["ldap"]) conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) conn.sasl_quiet = true conn.start_tls KRB_LOCK.synchronize do begin kg = Krb5Gssapi.new(CONFIG["fqdn"], CONFIG["keytab"]) kg.copy_ticket(ticket) username, org = kg.display_name.split("@") settings[:credentials][:username] = username LdapModel.setup(:organisation => PuavoRest::Organisation.by_domain(org.downcase)) conn.sasl_bind('', 'GSSAPI') rescue GSSAPI::GssApiError => err if err..match(/Clock skew too great/) raise KerberosError, :user => "Your clock is messed up" else raise KerberosError, :user => err. end rescue Krb5Gssapi::NoDelegation => err raise KerberosError, :user => "Credentials are not delegated! '--delegation always' missing?" ensure kg.clean_up end end conn end |
+ (Object) search(keywords)
Search for models using keywords
keywords
can be a string of +
or space separated keywords or an array
of keywords. Those models are returned which have a match for all the
keywords.
Each model using this method must implement a search_filters method which returns an array of lambdas (Proc) which generate the approciate ldap search filters. See create_filter_lambda
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/ldapmodel/search.rb', line 245 def self.search(keywords) if keywords.kind_of?(String) keywords = keywords.gsub("+", " ").split(" ") end return [] if keywords.nil? return [] if keywords.empty? filter_string = "(&" + keywords.map do |keyword| "(|" + search_filters.map do |sf| sf.call(keyword) end.join("") + ")" end.join("") + ")" filter(filter_string) end |
+ (Array<Proc>) search_filters
232 233 234 |
# File 'lib/ldapmodel/search.rb', line 232 def self.search_filters raise "search_filters not implemented for #{ self }" end |
+ (Object) settings
93 94 95 |
# File 'lib/ldapmodel/connection.rb', line 93 def self.settings Thread.current[:ldap_hash_settings] || { :credentials_cache => {} } end |
+ (Object) settings=(settings)
97 98 99 |
# File 'lib/ldapmodel/connection.rb', line 97 def self.settings=(settings) Thread.current[:ldap_hash_settings] = settings end |
+ (Object) setup(opts, &block)
Configure LDAP bind for LdapModel
111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/ldapmodel/connection.rb', line 111 def self.setup(opts, &block) prev = self.settings self.settings = prev.merge(opts) if opts[:credentials] self.settings[:credentials_cache] = {} end if block res = block.call self.settings = prev end res end |
+ (Object) skip_serialize(*attrs)
Skip this attribute(s) from serializations such as to_hash
and to_json
142 143 144 |
# File 'lib/ldapmodel/attr_conv.rb', line 142 def self.skip_serialize(*attrs) attrs.each { |a| skip_serialize_attrs[a.to_sym] = true } end |
Instance Method Details
- (Object) [](pretty_name)
325 326 327 |
# File 'lib/ldapmodel/attr_conv.rb', line 325 def [](pretty_name) send(pretty_name.to_sym) end |
- (Object) []=(pretty_name, value)
330 331 332 |
# File 'lib/ldapmodel/attr_conv.rb', line 330 def []=(pretty_name, value) set(pretty_name, value) end |
- (Object) add(pretty_name, value)
Append value to LdapConverters::ArrayValue attribute. Value is persisted on the next #save! call
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/ldapmodel/attr_conv.rb', line 238 def add(pretty_name, value) pretty_name = pretty_name.to_sym ldap_name = pretty2ldap[pretty_name] transform = [pretty_name][:transform] # if not LdapConverters::ArrayValue or subclass of it if !(transform <= LdapConverters::ArrayValue) raise "add! can be called only on LdapConverters::ArrayValue values. Not #{ transform }" end if new? raise "Cannot call add on new models. Just set the attribute directly" end if @previous_values[pretty_name].nil? @previous_values[pretty_name] = send(pretty_name) end value = transform.new(self).write(value) @pending_mods.push(LDAP::Mod.new(LDAP::LDAP_MOD_ADD, ldap_name.to_s, value)) @cache[pretty_name] = nil current_val = @ldap_attr_store[ldap_name.to_sym] @ldap_attr_store[ldap_name.to_sym] = Array(current_val) + value end |
- (Object) add_validation_error(attr, code, message)
Add validation error. Error will be raised on the next #save! call
307 308 309 310 311 312 313 314 315 |
# File 'lib/ldapmodel/attr_conv.rb', line 307 def add_validation_error(attr, code, ) current = @validation_errors[attr.to_sym] ||= [] current = current.select{|err| err[:code] != code} current.push( :code => code, :message => ) current = @validation_errors[attr.to_sym] = current end |
- (Object) as_json
Returns Object
416 417 418 |
# File 'lib/ldapmodel/attr_conv.rb', line 416 def as_json(*) to_hash end |
- (Object) assert_validation(message = nil)
Raises ValidationError if #add_validation_error was called at least once
448 449 450 451 452 453 454 455 456 457 458 |
# File 'lib/ldapmodel/attr_conv.rb', line 448 def assert_validation(=nil) return if @validation_errors.empty? errors = @validation_errors @validation_errors = {} raise ValidationError, { :message => || "Validation error", :className => self.class.name, :dn => dn, :invalid_attributes => errors } end |
- (Boolean) changed?(pretty_name)
Returns true if this value is going to be written to ldap on next #save! call
219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/ldapmodel/attr_conv.rb', line 219 def changed?(pretty_name) pretty_name = pretty_name.to_sym ldap_name = pretty2ldap[pretty_name] if !respond_to?(pretty_name) raise NoMethodError, "undefined method `#{ pretty_name }' for #{ self.class }" end return true if new? return false if !@previous_values.key?(ldap_name) current_val = send(pretty_name) prev_val = @previous_values[pretty_name] return current_val != prev_val end |
- (Object) create!(_dn = nil)
Save new model to LDAP
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/ldapmodel/attr_conv.rb', line 265 def create!(_dn=nil) if @existing raise "Cannot call create! on existing model" end validate!("Creating") run_hook :before, :create _dn = dn if _dn.nil? mods = @pending_mods.select do |mod| mod.mod_type != "dn" end res = self.class.ldap_op(:add, dn, mods) reset_pending @existing = true run_hook :after, :create res end |
- (Boolean) dirty?
Returns true when the model has unsaved changes in attributes
320 321 322 |
# File 'lib/ldapmodel/attr_conv.rb', line 320 def dirty? !@pending_mods.empty? end |
- (Boolean) empty?
Returns trur when the model has no values
336 337 338 |
# File 'lib/ldapmodel/attr_conv.rb', line 336 def empty? @ldap_attr_store.empty? end |
- (Object) get_own(pretty_name)
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/ldapmodel/attr_conv.rb', line 148 def get_own(pretty_name) pretty_name = pretty_name.to_sym return @cache[pretty_name] if not @cache[pretty_name].nil? ldap_name = pretty2ldap[pretty_name] default_value = [pretty_name][:default] value = Array(@ldap_attr_store[ldap_name]) # String values in our LDAP are always UTF-8 value = value.map do |item| if item.respond_to?(:force_encoding) item.force_encoding("UTF-8") else item end end if Array(value).empty? && !default_value.nil? return default_value end @cache[pretty_name] = transform(pretty_name, :read, value) end |
- (Object) get_raw(ldap_name)
193 194 195 |
# File 'lib/ldapmodel/attr_conv.rb', line 193 def get_raw(ldap_name) @ldap_attr_store[ldap_name.to_sym] end |
- (Object) ldap_merge!(hash)
Merge hash of ldap attributes to this model
361 362 363 364 365 366 |
# File 'lib/ldapmodel/attr_conv.rb', line 361 def ldap_merge!(hash) hash.each do |ldap_name, value| ldap_set(ldap_name, value) end self end |
- (Object) ldap_set(ldap_name, value)
Set attribute using the original ldap attribute
349 350 351 352 |
# File 'lib/ldapmodel/attr_conv.rb', line 349 def ldap_set(ldap_name, value) return if ldap2pretty[ldap_name.to_sym].nil? @ldap_attr_store[ldap_name.to_sym] = value end |
- (Object) link(path)
214 215 216 |
# File 'lib/ldapmodel/connection.rb', line 214 def link(path) self.class.settings[:rest_root] + path end |
- (Object) merge(other)
Merge value from other LdapModel to this one
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/ldapmodel/attr_conv.rb', line 370 def merge(other) h = nil _serialize_attrs = nil if other.kind_of?(self.class) h = other.ldap_attr_store _serialize_attrs = other.serialize_attrs else h = other # Assume something Hash like end _ldap_attrs = @ldap_attr_store.dup h.each do |pretty_name, value| _ldap_attrs[pretty2ldap[pretty_name.to_sym]] = value end self.class.new({}, { :serialize => _serialize_attrs, :store => _ldap_attrs }) end |
- (Boolean) new?
Returns true if the model is present in LDAP
52 53 54 |
# File 'lib/ldapmodel/attr_conv.rb', line 52 def new? !@existing end |
- (Object) object_model
426 427 428 |
# File 'lib/ldapmodel/attr_conv.rb', line 426 def object_model self.class.to_s end |
- (Object) save!
Save changes to LDAP
289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/ldapmodel/attr_conv.rb', line 289 def save! return create! if !@existing validate!("Updating") run_hook :before, :update res = self.class.ldap_op(:modify, dn, @pending_mods) reset_pending run_hook :after, :update res end |
- (Object) set(pretty_name, value)
355 356 357 |
# File 'lib/ldapmodel/attr_conv.rb', line 355 def set(pretty_name, value) @cache[pretty_name.to_sym] = value end |
- (Object) to_hash
Convert model to Hash
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
# File 'lib/ldapmodel/attr_conv.rb', line 394 def to_hash h = {} pretty2ldap.each do |pretty_name, _| next if @serialize_attrs && !@serialize_attrs.include?(pretty_name) if !skip_serialize_attrs[pretty_name.to_sym] h[pretty_name.to_s] = send(pretty_name) end end computed_attributes.each do |method_name, serialize_name| next if @serialize_attrs && !@serialize_attrs.include?(serialize_name) h[serialize_name.to_s] = send(method_name) end h end |
- (Object) to_json
Returns String
421 422 423 |
# File 'lib/ldapmodel/attr_conv.rb', line 421 def to_json(*) as_json.to_json end |
- (Object) to_ldap_hash
411 412 413 |
# File 'lib/ldapmodel/attr_conv.rb', line 411 def to_ldap_hash @ldap_attr_store.dup end |
- (Object) transform(pretty_name, method, value)
Transform value using the attribute transformer
179 180 181 182 |
# File 'lib/ldapmodel/attr_conv.rb', line 179 def transform(pretty_name, method, value) transformer = [pretty_name][:transform] transformer.new(self).send(method, value) end |
- (Object) update!(h)
185 186 187 188 189 |
# File 'lib/ldapmodel/attr_conv.rb', line 185 def update!(h) h.each do |k,v| send((k.to_s + "=").to_sym, v) end end |
- (Object) validate
Validation method called before saving. Override it and call #add_validation_error for any errors
432 433 |
# File 'lib/ldapmodel/attr_conv.rb', line 432 def validate end |
- (Object) validate!(message = nil)
Run hooks and validations. May raise ValidationError
462 463 464 465 466 467 |
# File 'lib/ldapmodel/attr_conv.rb', line 462 def validate!(=nil) run_hook :before, :validate validate assert_validation() run_hook :after, :validate end |
- (Object) validate_unique(pretty_name)
Validate uniqueness of an attribute
437 438 439 440 441 442 443 444 |
# File 'lib/ldapmodel/attr_conv.rb', line 437 def validate_unique(pretty_name) return if !changed?(pretty_name) ldap_name = pretty2ldap[pretty_name.to_sym] val = Array(get_raw(ldap_name)).first if self.class.by_attr(pretty_name, val) add_validation_error(pretty_name, "#{ pretty_name.to_s }_not_unique".to_sym, "#{ pretty_name }=#{ val } is not unique") end end |
- (Object) write_raw(ldap_name, new_val)
Write raw ldap value
201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/ldapmodel/attr_conv.rb', line 201 def write_raw(ldap_name, new_val) ldap_name = ldap_name.to_sym pretty_name = ldap2pretty[ldap_name] if pretty_name @previous_values[pretty_name] = send(pretty_name) @cache[pretty_name] = nil end @ldap_attr_store[ldap_name] = new_val @pending_mods.push(LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE, ldap_name.to_s, new_val)) new_val end |