treequel 1.5.3 → 1.6.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.
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/ruby
2
+ #encoding: utf-8
2
3
 
3
4
  require 'uri'
4
5
  require 'ldap'
@@ -235,8 +236,8 @@ module Treequel::Constants
235
236
  #
236
237
  # SP = 1*SPACE ; one or more " "
237
238
  # WSP = 0*SPACE ; zero or more " "
238
- SP = '[ ]+'
239
- WSP = '[ ]*'
239
+ SP = '\x20+'
240
+ WSP = '\x20*'
240
241
 
241
242
  ### These are inlined for simplicity
242
243
  # NULL = %x00 ; null (0)
@@ -312,9 +313,9 @@ module Treequel::Constants
312
313
  # UTF0 = %x80-BF
313
314
  # UTF1 = %x00-7F
314
315
  # UTF2 = %xC2-DF UTF0
315
- UTF0 = /[\x80-\xbf]/
316
- UTF1 = /[\x00-\x7f]/
317
- UTF2 = /[\xc2-\xdf] #{UTF0}/x
316
+ UTF0 = /[\x80-\xbf]/n
317
+ UTF1 = /[\x00-\x7f]/n
318
+ UTF2 = /[\xc2-\xdf] #{UTF0}/xn
318
319
 
319
320
  # UTF3 = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) / %xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
320
321
  UTF3 = /
@@ -325,7 +326,7 @@ module Treequel::Constants
325
326
  \xed [\x80-\x9f] #{UTF0}
326
327
  |
327
328
  [\xee-\xef] #{UTF0}{2}
328
- /x
329
+ /xn
329
330
 
330
331
  # UTF4 = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) / %xF4 %x80-8F 2(UTF0)
331
332
  UTF4 = /
@@ -334,7 +335,7 @@ module Treequel::Constants
334
335
  [\xf1-\xf3] #{UTF0}{3}
335
336
  |
336
337
  \xf4 [\x80-\x8f] #{UTF0}{2}
337
- /x
338
+ /xn
338
339
 
339
340
  # UTFMB = UTF2 / UTF3 / UTF4
340
341
  UTFMB = Regexp.union( UTF2, UTF3, UTF4 )
@@ -349,7 +350,8 @@ module Treequel::Constants
349
350
  LEADKEYCHAR = /[#{ALPHA}]/
350
351
 
351
352
  # keychar = ALPHA / DIGIT / HYPHEN
352
- KEYCHAR = /[#{ALPHA}#{DIGIT}\-]/
353
+ # NOTE: added literal '.' to work around OpenDS's non-standard matchingRule names
354
+ KEYCHAR = /[#{ALPHA}#{DIGIT}\-\.]/
353
355
 
354
356
  # number = DIGIT / ( LDIGIT 1*DIGIT )
355
357
  NUMBER = /[#{LDIGIT}]#{DIGIT}+|#{DIGIT}/ # Reversed for greediness
@@ -436,6 +438,23 @@ module Treequel::Constants
436
438
  QDSTRINGLIST = /(?: #{QDSTRING} (?: #{SP} #{QDSTRING} )* )?/x
437
439
  QDSTRINGS = / #{QDSTRING} | #{LPAREN} #{WSP} #{QDSTRINGLIST} #{WSP} #{RPAREN} /x
438
440
 
441
+ # Workaround for attributeType declarations that have unescaped single quotes
442
+ # in them (e.g., "Change Record Object Class Definition",
443
+ # http://tools.ietf.org/html/draft-good-ldap-changelog-04)
444
+ # It will accept an unquoted single quote as long as it's followed by
445
+ # a non-whitespace character.
446
+ MALFORMED_DSTRING = %r{
447
+ (?>
448
+ # An unescaped single quote followed by a non-whitespace character
449
+ #{SQUOTE} (?=\S)
450
+ |
451
+ # or correctly-escaped single-quoted string characters
452
+ #{DSTRING}
453
+ )*
454
+ }x
455
+ MALFORMED_QDSTRING = / #{SQUOTE} #{MALFORMED_DSTRING} #{SQUOTE} /x
456
+
457
+
439
458
  # extensions = *( SP xstring SP qdstrings )
440
459
  EXTENSIONS = /(?: #{SP} #{XSTRING} #{SP} #{QDSTRINGS} )*/x
441
460
 
@@ -455,9 +474,12 @@ module Treequel::Constants
455
474
  # [ SP "MAY" SP oids ] ; attribute types
456
475
  # extensions WSP RPAREN
457
476
 
477
+ # Note: added 'descr' to the oid to support Sun OpenDS, which allows names instead of
478
+ # numericoids "for convenience".
479
+ # (http://download.oracle.com/docs/cd/E19476-01/821-0509/object-class-description-format.html)
458
480
  LDAP_OBJECTCLASS_DESCRIPTION = %r{
459
481
  #{LPAREN} #{WSP}
460
- (#{NUMERICOID}) # $1 = oid
482
+ (#{NUMERICOID} | #{DESCR}) # $1 = oid
461
483
  (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
462
484
  (?:#{SP} DESC #{SP} (#{QDSTRING}))? # $3 = desc
463
485
  (?:#{SP} (OBSOLETE) )? # $4 = obsolete
@@ -469,17 +491,70 @@ module Treequel::Constants
469
491
  #{WSP} #{RPAREN}
470
492
  }x
471
493
 
494
+ # Support for objectClass definitions with the KIND before the SUP such as those
495
+ # in RFC3712
496
+ LDAP_MISORDERED_KIND_OBJECTCLASS_DESCRIPTION = %r{
497
+ #{LPAREN} #{WSP}
498
+ (#{NUMERICOID} | #{DESCR}) # $1 = oid
499
+ (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
500
+ (?:#{SP} DESC #{SP} (#{QDSTRING}))? # $3 = desc
501
+ (?:#{SP} (OBSOLETE) )? # $4 = obsolete
502
+ (?:#{SP} (#{KIND}) )? # $5 = kind
503
+ (?:#{SP} SUP #{SP} (#{OIDS}) )? # $6 = sup
504
+ (?:#{SP} MUST #{SP} (#{OIDS}) )? # $7 = must attrs
505
+ (?:#{SP} MAY #{SP} (#{OIDS}) )? # $8 = may attrs
506
+ (#{EXTENSIONS}) # $9 = extensions
507
+ #{WSP} #{RPAREN}
508
+ }x
509
+
510
+ # Support for objectClass definitions with the KIND after the MUST and MAY
511
+ # sections like RFC2696's authPasswordObject
512
+ LDAP_TRAILING_KIND_OBJECTCLASS_DESCRIPTION = %r{
513
+ #{LPAREN} #{WSP}
514
+ (#{NUMERICOID} | #{DESCR}) # $1 = oid
515
+ (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
516
+ (?:#{SP} DESC #{SP} (#{QDSTRING}))? # $3 = desc
517
+ (?:#{SP} (OBSOLETE) )? # $4 = obsolete
518
+ (?:#{SP} SUP #{SP} (#{OIDS}) )? # $5 = sup
519
+ (?:#{SP} MUST #{SP} (#{OIDS}) )? # $6 = must attrs
520
+ (?:#{SP} MAY #{SP} (#{OIDS}) )? # $7 = may attrs
521
+ (?:#{SP} (#{KIND}) )? # $8 = kind
522
+ (#{EXTENSIONS}) # $9 = extensions
523
+ #{WSP} #{RPAREN}
524
+ }x
525
+
526
+ # Support for objectClass definitions with the DESC after the SUP and KIND
527
+ # like draft-howard-rfc2307bis, and a bunch of "Solaris Specific" ones from
528
+ # OpenDS servers.
529
+ LDAP_MISORDERED_DESC_OBJECTCLASS_DESCRIPTION = %r{
530
+ #{LPAREN} #{WSP}
531
+ (#{NUMERICOID} | #{DESCR}) # $1 = oid
532
+ (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
533
+ (?:#{SP} (OBSOLETE) )? # $3 = obsolete
534
+ (?:#{SP} SUP #{SP} (#{OIDS}) )? # $4 = sup
535
+ (?:#{SP} (#{KIND}) )? # $5 = kind
536
+ (?:#{SP} DESC #{SP} (#{QDSTRING}))? # $6 = desc
537
+ (?:#{SP} MUST #{SP} (#{OIDS}) )? # $7 = must attrs
538
+ (?:#{SP} MAY #{SP} (#{OIDS}) )? # $8 = may attrs
539
+ (#{EXTENSIONS}) # $9 = extensions
540
+ #{WSP} #{RPAREN}
541
+ }x
542
+
472
543
 
473
544
  # usage = "userApplications" / ; user
474
545
  # "directoryOperation" / ; directory operational
475
546
  # "distributedOperation" / ; DSA-shared operational
476
547
  # "dSAOperation" ; DSA-specific operational
477
- USAGE = Regexp.union(
478
- 'userApplications',
479
- 'directoryOperation',
480
- 'distributedOperation',
481
- 'dSAOperation'
482
- )
548
+ USAGE = %r{
549
+ userApplications
550
+ |
551
+ directoryOperation
552
+ |
553
+ distributedOperation
554
+ |
555
+ dSAOperation
556
+ }xi
557
+
483
558
 
484
559
  # Attribute Type definitions are written according to the ABNF:
485
560
  #
@@ -498,9 +573,13 @@ module Treequel::Constants
498
573
  # [ SP "NO-USER-MODIFICATION" ] ; not user modifiable
499
574
  # [ SP "USAGE" SP usage ] ; usage
500
575
  # extensions WSP RPAREN ; extensions
576
+
577
+ # Note: added 'descr' to the oid to support Sun OpenDS, which allows names instead of
578
+ # numericoids "for convenience".
579
+ # (https://www.opends.org/wiki/page/UnderstandingAttributeTypes)
501
580
  LDAP_ATTRIBUTE_TYPE_DESCRIPTION = %r{
502
581
  #{LPAREN} #{WSP}
503
- (#{NUMERICOID}) # $1 = oid
582
+ (#{NUMERICOID} | #{DESCR}) # $1 = oid
504
583
  (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
505
584
  (?:#{SP} DESC #{SP} (#{QDSTRING}) )? # $3 = description
506
585
  (?:#{SP} (OBSOLETE) )? # $4 = obsolete flag
@@ -517,6 +596,50 @@ module Treequel::Constants
517
596
  #{WSP} #{RPAREN}
518
597
  }x
519
598
 
599
+ # Attribute type with an unescaped single quote in the DESC; added for schemas that
600
+ # include the 'changelog' attributeType from
601
+ # http://tools.ietf.org/html/draft-good-ldap-changelog-04
602
+ LDAP_UNESCAPE_SQUOTE_ATTRIBUTE_TYPE_DESCRIPTION = %r{
603
+ #{LPAREN} #{WSP}
604
+ (#{NUMERICOID} | #{DESCR}) # $1 = oid
605
+ (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
606
+ (?:#{SP} DESC #{SP} (#{MALFORMED_QDSTRING}) ) # $3 = description
607
+ (?:#{SP} (OBSOLETE) )? # $4 = obsolete flag
608
+ (?:#{SP} SUP #{SP} (#{OID}) )? # $5 = superior type oid
609
+ (?:#{SP} EQUALITY #{SP} (#{OID}) )? # $6 = equality matching rule oid
610
+ (?:#{SP} ORDERING #{SP} (#{OID}) )? # $7 = ordering matching rule oid
611
+ (?:#{SP} SUBSTR #{SP} (#{OID}) )? # $8 = substring matching rule oid
612
+ (?:#{SP} SYNTAX #{SP} (#{NOIDLEN}) )? # $9 = value syntax matching oid
613
+ (?:#{SP} (SINGLE-VALUE) )? # $10 = single value flag
614
+ (?:#{SP} (COLLECTIVE) )? # $11 = collective flag
615
+ (?:#{SP} (NO-USER-MODIFICATION) )? # $12 = no user modification flag
616
+ (?:#{SP} USAGE #{SP} (#{USAGE}) )? # $13 = usage type
617
+ (#{EXTENSIONS}) # $14 = extensions
618
+ #{WSP} #{RPAREN}
619
+ }x
620
+
621
+ # Support for attributeType declarations which have the SYNTAX before the EQUALITY
622
+ # and ORDERING (e.g., changeNumber from
623
+ # http://tools.ietf.org/html/draft-good-ldap-changelog-04 )
624
+ LDAP_MISORDERED_SYNTAX_ATTRIBUTE_TYPE_DESCRIPTION = %r{
625
+ #{LPAREN} #{WSP}
626
+ (#{NUMERICOID} | #{DESCR}) # $1 = oid
627
+ (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
628
+ (?:#{SP} DESC #{SP} (#{QDSTRING}) )? # $3 = description
629
+ (?:#{SP} (OBSOLETE) )? # $4 = obsolete flag
630
+ (?:#{SP} SUP #{SP} (#{OID}) )? # $5 = superior type oid
631
+ (?:#{SP} SYNTAX #{SP} (#{NOIDLEN}) )? # $6 = value syntax matching oid
632
+ (?:#{SP} EQUALITY #{SP} (#{OID}) )? # $7 = equality matching rule oid
633
+ (?:#{SP} ORDERING #{SP} (#{OID}) )? # $8 = ordering matching rule oid
634
+ (?:#{SP} SUBSTR #{SP} (#{OID}) )? # $9 = substring matching rule oid
635
+ (?:#{SP} (SINGLE-VALUE) )? # $10 = single value flag
636
+ (?:#{SP} (COLLECTIVE) )? # $11 = collective flag
637
+ (?:#{SP} (NO-USER-MODIFICATION) )? # $12 = no user modification flag
638
+ (?:#{SP} USAGE #{SP} (#{USAGE}) )? # $13 = usage type
639
+ (#{EXTENSIONS}) # $14 = extensions
640
+ #{WSP} #{RPAREN}
641
+ }x
642
+
520
643
 
521
644
  # MatchingRuleDescription = LPAREN WSP
522
645
  # numericoid ; object identifier
@@ -527,12 +650,12 @@ module Treequel::Constants
527
650
  # extensions WSP RPAREN ; extensions
528
651
  LDAP_MATCHING_RULE_DESCRIPTION = %r{
529
652
  #{LPAREN} #{WSP}
530
- (#{NUMERICOID}) # $1 = oid
531
- (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
532
- (?:#{SP} DESC #{SP} (#{QDSTRING}) )? # $3 = description
533
- (?:#{SP} (OBSOLETE) )? # $4 = obsolete flag
534
- #{SP} SYNTAX #{SP} (#{NUMERICOID}) # $5 = syntax numeric OID
535
- (#{EXTENSIONS}) # $6 = extensions
653
+ (#{NUMERICOID}) # $1 = oid
654
+ (?:#{SP} NAME #{SP} (#{QDESCRS}) )? # $2 = name
655
+ (?:#{SP} DESC #{SP} (#{QDSTRING}) )? # $3 = description
656
+ (?:#{SP} (OBSOLETE) )? # $4 = obsolete flag
657
+ #{SP} SYNTAX #{SP} (#{NUMERICOID}) # $5 = syntax numeric OID
658
+ (#{EXTENSIONS}) # $6 = extensions
536
659
  #{WSP} #{RPAREN}
537
660
  }x
538
661
 
@@ -147,6 +147,18 @@ class Treequel::Directory
147
147
  end
148
148
 
149
149
 
150
+ ### Copy constructor -- the duplicate should have a distinct connection, bound user,
151
+ ### and should have a distinct copy of the +original+'s registered controls.
152
+ def initialize_copy( original )
153
+ @conn = nil
154
+ @bound_user = nil
155
+
156
+ @object_conversions = @object_conversions.dup
157
+ @attribute_conversions = @attribute_conversions.dup
158
+ @registered_controls = @registered_controls.dup
159
+ end
160
+
161
+
150
162
  ######
151
163
  public
152
164
  ######
@@ -370,6 +370,9 @@ class Treequel::Model < Treequel::Branch
370
370
  self.errors.add( :objectClass, 'must have at least one' ) if self.object_classes.empty?
371
371
 
372
372
  super( options )
373
+ self.log.debug "Validations failed:\s %s" % [ self.errors.full_messages.join("\n ") ] if
374
+ self.errors.count.nonzero?
375
+
373
376
  self.after_validation
374
377
  end
375
378
 
@@ -395,8 +398,10 @@ class Treequel::Model < Treequel::Branch
395
398
  raise Treequel::BeforeHookFailed, :save
396
399
 
397
400
  if self.exists?
401
+ self.log.debug " already exists, so updating."
398
402
  self.update( mods )
399
403
  else
404
+ self.log.debug " doesn't exist, so creating."
400
405
  self.create( mods )
401
406
  end
402
407
 
@@ -59,13 +59,25 @@ class Treequel::Schema::AttributeType
59
59
 
60
60
  ### Parse an AttributeType entry from a attributeType description from a schema.
61
61
  def self::parse( schema, description )
62
- unless match = ( LDAP_ATTRIBUTE_TYPE_DESCRIPTION.match(description) )
62
+ oid, names, desc, obsolete, sup_oid, eqmatch_oid, ordmatch_oid, submatch_oid, syntax_oid,
63
+ single, collective, nousermod, usagetype, extensions = nil
64
+
65
+ case description.gsub( /[\n\t]+/, ' ' ).squeeze( ' ' )
66
+ when LDAP_ATTRIBUTE_TYPE_DESCRIPTION
67
+ oid, names, desc, obsolete, sup_oid, eqmatch_oid, ordmatch_oid, submatch_oid, syntax_oid,
68
+ single, collective, nousermod, usagetype, extensions = $~.captures
69
+ when LDAP_UNESCAPE_SQUOTE_ATTRIBUTE_TYPE_DESCRIPTION
70
+ oid, names, desc, obsolete, sup_oid, eqmatch_oid, ordmatch_oid, submatch_oid, syntax_oid,
71
+ single, collective, nousermod, usagetype, extensions = $~.captures
72
+ self.handle_malformed_parse( "unescaped single quote in DESC #{desc}", description )
73
+ when LDAP_MISORDERED_SYNTAX_ATTRIBUTE_TYPE_DESCRIPTION
74
+ oid, names, desc, obsolete, sup_oid, syntax_oid, eqmatch_oid, ordmatch_oid, submatch_oid,
75
+ single, collective, nousermod, usagetype, extensions = $~.captures
76
+ self.handle_malformed_parse( "misordered SYNTAX #{syntax_oid}", description )
77
+ else
63
78
  raise Treequel::ParseError, "failed to parse attributeType from %p" % [ description ]
64
79
  end
65
80
 
66
- oid, names, desc, obsolete, sup_oid, eqmatch_oid, ordmatch_oid, submatch_oid, syntax_oid,
67
- single, collective, nousermod, usagetype, extensions = match.captures
68
-
69
81
  # Normalize the attributes
70
82
  names = Treequel::Schema.parse_names( names )
71
83
  desc = Treequel::Schema.unquote_desc( desc )
@@ -94,6 +106,18 @@ class Treequel::Schema::AttributeType
94
106
  end
95
107
 
96
108
 
109
+ ### Handle the parse of an attributeType that matches one of the non-standard attributeType
110
+ ### definitions found in several RFCs. If Treequel::Schema.strict_parse_mode? is +true+,
111
+ ### this method will raise an exception.
112
+ def self::handle_malformed_parse( message, attr_desc )
113
+ raise Treequel::ParseError, "Malformed attributeType: %s: %p" % [ message, attr_desc ] if
114
+ Treequel::Schema.strict_parse_mode?
115
+ Treequel.log.info "Working around malformed attributeType: %s: %p" % [ message, attr_desc ]
116
+ end
117
+
118
+
119
+
120
+
97
121
  #############################################################
98
122
  ### I N S T A N C E M E T H O D S
99
123
  #############################################################
@@ -52,19 +52,41 @@ class Treequel::Schema
52
52
  end
53
53
 
54
54
 
55
- ### Parse an ObjectClass entry from a objectClass description from a schema.
55
+ ### Parse an ObjectClass entry from a objectClass +description+ from a +schema+.
56
+ ### @param [String] description the RFC4512-format objectClass description
57
+ ### @param [Treequel::Schema] schema the schema object the objectClass belongs to
58
+ ### @return [Treequel::Schema::ObjectClass] the resulting objectclass
56
59
  def self::parse( schema, description )
57
- unless match = ( LDAP_OBJECTCLASS_DESCRIPTION.match(description) )
60
+ oid, names, desc, obsolete, sup, kind, must, may, extensions = nil
61
+
62
+ # :FIXME: Change this to some sort of strategy that extracts the pieces from the
63
+ # description and checks to be sure everything was consumed instead of depending
64
+ # on the RFC's BNF. It appears people expect to be able to arbitrarily reorder
65
+ # them, and making a different Regexp for each exception isn't going to work
66
+ # long-term.
67
+ case description.gsub( /[\n\t]+/, ' ' ).squeeze( ' ' )
68
+ when LDAP_OBJECTCLASS_DESCRIPTION
69
+ oid, names, desc, obsolete, sup, kind, must, may, extensions = $~.captures
70
+ when LDAP_MISORDERED_KIND_OBJECTCLASS_DESCRIPTION
71
+ oid, names, desc, obsolete, kind, sup, must, may, extensions = $~.captures
72
+ self.handle_malformed_parse( "transposed KIND (#{kind}) and SUP (#{sup})",
73
+ description )
74
+ when LDAP_TRAILING_KIND_OBJECTCLASS_DESCRIPTION
75
+ oid, names, desc, obsolete, sup, must, may, kind, extensions = $~.captures
76
+ self.handle_malformed_parse( "misordered KIND (#{kind})", description )
77
+ when LDAP_MISORDERED_DESC_OBJECTCLASS_DESCRIPTION
78
+ oid, names, obsolete, sup, kind, desc, must, may, extensions = $~.captures
79
+ self.handle_malformed_parse( "misordered DESC (#{desc})", description )
80
+ else
58
81
  raise Treequel::ParseError, "failed to parse objectClass from %p" % [ description ]
59
82
  end
60
83
 
61
- oid, names, desc, obsolete, sup, kind, must, may, extensions = match.captures
62
-
63
84
  # Normalize the attributes
64
- must_oids = Treequel::Schema.parse_oids( must )
65
- may_oids = Treequel::Schema.parse_oids( may )
66
- names = Treequel::Schema.parse_names( names )
67
- desc = Treequel::Schema.unquote_desc( desc )
85
+ must_oids = Treequel::Schema.parse_oids( must )
86
+ may_oids = Treequel::Schema.parse_oids( may )
87
+ names = Treequel::Schema.parse_names( names )
88
+ desc = Treequel::Schema.unquote_desc( desc )
89
+ extensions = extensions.strip
68
90
 
69
91
  # Default the 'kind' attribute
70
92
  kind ||= DEFAULT_OBJECTCLASS_KIND
@@ -79,6 +101,16 @@ class Treequel::Schema
79
101
  end
80
102
 
81
103
 
104
+ ### Handle the parse of an objectClass that matches one of the non-standard objectClass
105
+ ### definitions found in several RFCs. If Treequel::Schema.strict_parse_mode? is +true+,
106
+ ### this method will raise an exception.
107
+ def self::handle_malformed_parse( message, oc_desc )
108
+ raise Treequel::ParseError, "Malformed objectClass: %s: %p" % [ message, oc_desc ] if
109
+ Treequel::Schema.strict_parse_mode?
110
+ Treequel.log.info "Working around malformed objectClass: %s: %p" % [ message, oc_desc ]
111
+ end
112
+
113
+
82
114
  #############################################################
83
115
  ### I N S T A N C E M E T H O D S
84
116
  #############################################################
@@ -37,6 +37,24 @@ class Treequel::Schema
37
37
  ### C L A S S M E T H O D S
38
38
  #################################################################
39
39
 
40
+ @strict_parse_mode = false
41
+
42
+ ### Set the strict-parsing +flag+. Setting this to a +true+ value causes schema-parsing
43
+ ### errors to be propagated to the caller instead of handled by the constructor, which is
44
+ ### the default behavior.
45
+ ### @param [boolean] flag the new flag value
46
+ def self::strict_parse_mode=( newval )
47
+ @strict_parse_mode = newval ? true : false
48
+ end
49
+
50
+
51
+ ### Test whether or not strict-parsing mode is in effect.
52
+ ### @return [boolean] false if parse errors will be caught
53
+ def self::strict_parse_mode?
54
+ return @strict_parse_mode ? true : false
55
+ end
56
+
57
+
40
58
  ### Parse the given +oidstring+ into an Array of OIDs, with Strings for numeric OIDs and
41
59
  ### Symbols for aliases.
42
60
  def self::parse_oids( oidstring )
@@ -162,11 +180,11 @@ class Treequel::Schema
162
180
  ### keys "objectClasses", "ldapSyntaxes", "matchingRuleUse", "attributeTypes", and
163
181
  ### "matchingRules".
164
182
  def initialize( hash )
165
- @object_classes = self.parse_objectclasses( hash['objectClasses'] )
166
- @attribute_types = self.parse_attribute_types( hash['attributeTypes'] )
167
- @ldap_syntaxes = self.parse_ldap_syntaxes( hash['ldapSyntaxes'] )
168
- @matching_rules = self.parse_matching_rules( hash['matchingRules'] )
169
- @matching_rule_uses = self.parse_matching_rule_uses( hash['matchingRuleUse'] )
183
+ @object_classes = self.parse_objectclasses( hash['objectClasses'] || [] )
184
+ @attribute_types = self.parse_attribute_types( hash['attributeTypes'] || [] )
185
+ @ldap_syntaxes = self.parse_ldap_syntaxes( hash['ldapSyntaxes'] || [] )
186
+ @matching_rules = self.parse_matching_rules( hash['matchingRules'] || [] )
187
+ @matching_rule_uses = self.parse_matching_rule_uses( hash['matchingRuleUse'] || [] )
170
188
  end
171
189
 
172
190
 
@@ -237,11 +255,17 @@ class Treequel::Schema
237
255
  ### has any).
238
256
  def parse_objectclasses( descriptions )
239
257
  return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
240
- oc = Treequel::Schema::ObjectClass.parse( self, desc ) or
241
- raise Treequel::Error, "couldn't create an objectClass from %p" % [ desc ]
242
-
243
- table[ oc.oid ] = oc
244
- oc.names.inject( table ) {|h, name| h[name] = oc; h }
258
+ begin
259
+ oc = Treequel::Schema::ObjectClass.parse( self, desc )
260
+ table[ oc.oid ] = oc
261
+ oc.names.inject( table ) {|h, name| h[name] = oc; h }
262
+ rescue Treequel::ParseError => err
263
+ if self.class.strict_parse_mode?
264
+ raise
265
+ else
266
+ self.log.warn( err.message )
267
+ end
268
+ end
245
269
 
246
270
  table
247
271
  end
@@ -253,11 +277,17 @@ class Treequel::Schema
253
277
  ### (if it has any).
254
278
  def parse_attribute_types( descriptions )
255
279
  return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
256
- attrtype = Treequel::Schema::AttributeType.parse( self, desc ) or
257
- raise Treequel::Error, "couldn't create an attributeType from %p" % [ desc ]
258
-
259
- table[ attrtype.oid ] = attrtype
260
- attrtype.names.inject( table ) {|h, name| h[name] = attrtype; h }
280
+ begin
281
+ attrtype = Treequel::Schema::AttributeType.parse( self, desc )
282
+ table[ attrtype.oid ] = attrtype
283
+ attrtype.names.inject( table ) {|h, name| h[name] = attrtype; h }
284
+ rescue Treequel::ParseError => err
285
+ if self.class.strict_parse_mode?
286
+ raise
287
+ else
288
+ self.log.warn( err.message )
289
+ end
290
+ end
261
291
 
262
292
  table
263
293
  end
@@ -269,10 +299,17 @@ class Treequel::Schema
269
299
  def parse_ldap_syntaxes( descriptions )
270
300
  descriptions ||= []
271
301
  return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
272
- syntax = Treequel::Schema::LDAPSyntax.parse( self, desc ) or
273
- raise Treequel::Error, "couldn't create an LDAPSyntax from %p" % [ desc ]
302
+ begin
303
+ syntax = Treequel::Schema::LDAPSyntax.parse( self, desc )
304
+ table[ syntax.oid ] = syntax
305
+ rescue Treequel::ParseError => err
306
+ if self.class.strict_parse_mode?
307
+ raise
308
+ else
309
+ self.log.warn( err.message )
310
+ end
311
+ end
274
312
 
275
- table[ syntax.oid ] = syntax
276
313
  table
277
314
  end
278
315
  end
@@ -284,11 +321,17 @@ class Treequel::Schema
284
321
  def parse_matching_rules( descriptions )
285
322
  descriptions ||= []
286
323
  return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
287
- rule = Treequel::Schema::MatchingRule.parse( self, desc ) or
288
- raise Treequel::Error, "couldn't create an matchingRule from %p" % [ desc ]
289
-
290
- table[ rule.oid ] = rule
291
- rule.names.inject( table ) {|h, name| h[name] = rule; h }
324
+ begin
325
+ rule = Treequel::Schema::MatchingRule.parse( self, desc )
326
+ table[ rule.oid ] = rule
327
+ rule.names.inject( table ) {|h, name| h[name] = rule; h }
328
+ rescue Treequel::ParseError => err
329
+ if self.class.strict_parse_mode?
330
+ raise
331
+ else
332
+ self.log.warn( err.message )
333
+ end
334
+ end
292
335
 
293
336
  table
294
337
  end
@@ -301,11 +344,17 @@ class Treequel::Schema
301
344
  def parse_matching_rule_uses( descriptions )
302
345
  descriptions ||= []
303
346
  return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
304
- ruleuse = Treequel::Schema::MatchingRuleUse.parse( self, desc ) or
305
- raise Treequel::Error, "couldn't create an matchingRuleUse from %p" % [ desc ]
306
-
307
- table[ ruleuse.oid ] = ruleuse
308
- ruleuse.names.inject( table ) {|h, name| h[name] = ruleuse; h }
347
+ begin
348
+ ruleuse = Treequel::Schema::MatchingRuleUse.parse( self, desc )
349
+ table[ ruleuse.oid ] = ruleuse
350
+ ruleuse.names.inject( table ) {|h, name| h[name] = ruleuse; h }
351
+ rescue Treequel::ParseError => err
352
+ if self.class.strict_parse_mode?
353
+ raise
354
+ else
355
+ self.log.warn( err.message )
356
+ end
357
+ end
309
358
 
310
359
  table
311
360
  end
data/lib/treequel.rb CHANGED
@@ -53,10 +53,10 @@ end
53
53
  module Treequel
54
54
 
55
55
  # Library version
56
- VERSION = '1.5.3'
56
+ VERSION = '1.6.0'
57
57
 
58
58
  # VCS revision
59
- REVISION = %q$Revision: 1f396aca7d8b $
59
+ REVISION = %q$Revision: 2639d7f96151 $
60
60
 
61
61
  # Common paths for ldap.conf
62
62
  COMMON_LDAP_CONF_PATHS = %w[