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.
- data/ChangeLog +181 -3
- data/{History.md → History.rdoc} +53 -19
- data/Manifest.txt +5 -2
- data/{README.md → README.rdoc} +0 -0
- data/Rakefile +55 -21
- data/bin/treewhat +4 -0
- data/lib/treequel/branch.rb +28 -1
- data/lib/treequel/branchset.rb +9 -0
- data/lib/treequel/constants.rb +145 -22
- data/lib/treequel/directory.rb +12 -0
- data/lib/treequel/model.rb +5 -0
- data/lib/treequel/schema/attributetype.rb +28 -4
- data/lib/treequel/schema/objectclass.rb +40 -8
- data/lib/treequel/schema.rb +77 -28
- data/lib/treequel.rb +2 -2
- data/spec/data/ad_schema.yml +1752 -0
- data/spec/data/opends.yml +1986 -0
- data/spec/data/ticket11.yml +17 -0
- data/spec/lib/helpers.rb +17 -2
- data/spec/treequel/branch_spec.rb +8 -1
- data/spec/treequel/branchset_spec.rb +9 -0
- data/spec/treequel/directory_spec.rb +4 -2
- data/spec/treequel/monkeypatches_spec.rb +1 -1
- data/spec/treequel/schema/attributetype_spec.rb +88 -11
- data/spec/treequel/schema/matchingrule_spec.rb +37 -0
- data/spec/treequel/schema/objectclass_spec.rb +85 -1
- data/spec/treequel/schema_spec.rb +145 -30
- data.tar.gz.sig +0 -0
- metadata +147 -222
- metadata.gz.sig +0 -0
data/lib/treequel/constants.rb
CHANGED
|
@@ -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}/
|
|
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
|
-
/
|
|
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
|
-
/
|
|
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
|
-
|
|
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})
|
|
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 =
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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})
|
|
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})
|
|
531
|
-
(?:#{SP} NAME #{SP} (#{QDESCRS}) )?
|
|
532
|
-
(?:#{SP} DESC #{SP} (#{QDSTRING}) )?
|
|
533
|
-
(?:#{SP} (OBSOLETE) )?
|
|
534
|
-
#{SP} SYNTAX #{SP} (#{NUMERICOID})
|
|
535
|
-
(#{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
|
|
data/lib/treequel/directory.rb
CHANGED
|
@@ -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
|
######
|
data/lib/treequel/model.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
65
|
-
may_oids
|
|
66
|
-
names
|
|
67
|
-
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
|
#############################################################
|
data/lib/treequel/schema.rb
CHANGED
|
@@ -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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
273
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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.
|
|
56
|
+
VERSION = '1.6.0'
|
|
57
57
|
|
|
58
58
|
# VCS revision
|
|
59
|
-
REVISION = %q$Revision:
|
|
59
|
+
REVISION = %q$Revision: 2639d7f96151 $
|
|
60
60
|
|
|
61
61
|
# Common paths for ldap.conf
|
|
62
62
|
COMMON_LDAP_CONF_PATHS = %w[
|