treequel 1.0.1 → 1.0.4

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.
Files changed (73) hide show
  1. data/ChangeLog +176 -14
  2. data/LICENSE +1 -1
  3. data/Rakefile +61 -45
  4. data/Rakefile.local +20 -0
  5. data/bin/treequel +502 -269
  6. data/examples/ldap-rack-auth.rb +2 -0
  7. data/lib/treequel.rb +221 -18
  8. data/lib/treequel/branch.rb +410 -201
  9. data/lib/treequel/branchcollection.rb +25 -13
  10. data/lib/treequel/branchset.rb +42 -40
  11. data/lib/treequel/constants.rb +233 -3
  12. data/lib/treequel/control.rb +95 -0
  13. data/lib/treequel/controls/contentsync.rb +138 -0
  14. data/lib/treequel/controls/pagedresults.rb +162 -0
  15. data/lib/treequel/controls/sortedresults.rb +216 -0
  16. data/lib/treequel/directory.rb +212 -65
  17. data/lib/treequel/exceptions.rb +11 -12
  18. data/lib/treequel/filter.rb +1 -12
  19. data/lib/treequel/mixins.rb +83 -47
  20. data/lib/treequel/monkeypatches.rb +29 -0
  21. data/lib/treequel/schema.rb +23 -19
  22. data/lib/treequel/schema/attributetype.rb +33 -3
  23. data/lib/treequel/schema/ldapsyntax.rb +0 -11
  24. data/lib/treequel/schema/matchingrule.rb +0 -11
  25. data/lib/treequel/schema/matchingruleuse.rb +0 -11
  26. data/lib/treequel/schema/objectclass.rb +36 -10
  27. data/lib/treequel/schema/table.rb +159 -0
  28. data/lib/treequel/sequel_integration.rb +7 -7
  29. data/lib/treequel/utils.rb +4 -66
  30. data/rake/documentation.rb +89 -0
  31. data/rake/helpers.rb +375 -307
  32. data/rake/hg.rb +16 -2
  33. data/rake/manual.rb +11 -6
  34. data/rake/packaging.rb +20 -35
  35. data/rake/publishing.rb +22 -62
  36. data/spec/lib/constants.rb +20 -0
  37. data/spec/lib/control_behavior.rb +44 -0
  38. data/spec/lib/matchers.rb +51 -0
  39. data/spec/treequel/branch_spec.rb +88 -29
  40. data/spec/treequel/branchcollection_spec.rb +24 -1
  41. data/spec/treequel/branchset_spec.rb +123 -51
  42. data/spec/treequel/control_spec.rb +48 -0
  43. data/spec/treequel/controls/contentsync_spec.rb +38 -0
  44. data/spec/treequel/controls/pagedresults_spec.rb +138 -0
  45. data/spec/treequel/controls/sortedresults_spec.rb +171 -0
  46. data/spec/treequel/directory_spec.rb +186 -16
  47. data/spec/treequel/mixins_spec.rb +42 -3
  48. data/spec/treequel/schema/attributetype_spec.rb +22 -20
  49. data/spec/treequel/schema/objectclass_spec.rb +67 -46
  50. data/spec/treequel/schema/table_spec.rb +134 -0
  51. data/spec/treequel_spec.rb +277 -15
  52. metadata +89 -108
  53. data/bin/treequel.orig +0 -963
  54. data/examples/ldap-monitor.rb +0 -143
  55. data/examples/ldap-monitor/public/css/master.css +0 -328
  56. data/examples/ldap-monitor/public/images/card_small.png +0 -0
  57. data/examples/ldap-monitor/public/images/chain_small.png +0 -0
  58. data/examples/ldap-monitor/public/images/globe_small.png +0 -0
  59. data/examples/ldap-monitor/public/images/globe_small_green.png +0 -0
  60. data/examples/ldap-monitor/public/images/plug.png +0 -0
  61. data/examples/ldap-monitor/public/images/shadows/large-30-down.png +0 -0
  62. data/examples/ldap-monitor/public/images/tick.png +0 -0
  63. data/examples/ldap-monitor/public/images/tick_circle.png +0 -0
  64. data/examples/ldap-monitor/public/images/treequel-favicon.png +0 -0
  65. data/examples/ldap-monitor/views/backends.erb +0 -41
  66. data/examples/ldap-monitor/views/connections.erb +0 -74
  67. data/examples/ldap-monitor/views/databases.erb +0 -39
  68. data/examples/ldap-monitor/views/dump_subsystem.erb +0 -14
  69. data/examples/ldap-monitor/views/index.erb +0 -14
  70. data/examples/ldap-monitor/views/layout.erb +0 -35
  71. data/examples/ldap-monitor/views/listeners.erb +0 -30
  72. data/rake/rdoc.rb +0 -30
  73. data/rake/win32.rb +0 -190
@@ -9,22 +9,14 @@ BEGIN {
9
9
  $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
10
  }
11
11
 
12
- begin
13
- require 'spec'
14
- require 'spec/lib/constants'
15
- require 'spec/lib/helpers'
16
-
17
- require 'yaml'
18
- require 'ldap'
19
- require 'ldap/schema'
20
- require 'treequel/schema/attributetype'
21
- rescue LoadError
22
- unless Object.const_defined?( :Gem )
23
- require 'rubygems'
24
- retry
25
- end
26
- raise
27
- end
12
+ require 'spec'
13
+ require 'spec/lib/constants'
14
+ require 'spec/lib/helpers'
15
+
16
+ require 'yaml'
17
+ require 'ldap'
18
+ require 'ldap/schema'
19
+ require 'treequel/schema/attributetype'
28
20
 
29
21
 
30
22
  include Treequel::TestConstants
@@ -40,7 +32,6 @@ describe Treequel::Schema::AttributeType do
40
32
 
41
33
  before( :all ) do
42
34
  setup_logging( :fatal )
43
- @datadir = Pathname( __FILE__ ).dirname.parent.parent + 'data'
44
35
  end
45
36
 
46
37
  before( :each ) do
@@ -185,7 +176,7 @@ describe Treequel::Schema::AttributeType do
185
176
 
186
177
  describe "parsed from an attributeType that has a list as the value of its NAME attribute" do
187
178
 
188
- MULTINAME_ATTRIBUTETYPE = %{( 1.1.1.1 NAME ('firstname' 'secondname') )}
179
+ MULTINAME_ATTRIBUTETYPE = %{( 1.1.1.1 NAME ('firstName' 'secondName') )}
189
180
 
190
181
  before( :each ) do
191
182
  @attrtype = Treequel::Schema::AttributeType.parse( @schema, MULTINAME_ATTRIBUTETYPE )
@@ -193,13 +184,24 @@ describe Treequel::Schema::AttributeType do
193
184
 
194
185
  it "knows what both names are" do
195
186
  @attrtype.names.should have(2).members
196
- @attrtype.names.should include( :firstname, :secondname )
187
+ @attrtype.names.should include( :firstName, :secondName )
197
188
  end
198
189
 
199
190
  it "returns the first of its names for the #name method" do
200
- @attrtype.name.should == :firstname
191
+ @attrtype.name.should == :firstName
192
+ end
193
+
194
+ it "knows how to build a normalized list of its names" do
195
+ @attrtype.normalized_names.should == [ :firstname, :secondname ]
196
+ end
197
+
198
+ it "knows when a name is valid" do
199
+ @attrtype.valid_name?( 'first name' ).should be_true()
201
200
  end
202
201
 
202
+ it "knows when a name is invalid" do
203
+ @attrtype.valid_name?( :name2 ).should be_false()
204
+ end
203
205
  end
204
206
 
205
207
  describe "parsed from an attributeType that has escaped characters in its DESC attribute" do
@@ -9,24 +9,15 @@ BEGIN {
9
9
  $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
10
  }
11
11
 
12
- begin
13
- require 'spec'
14
- require 'spec/lib/constants'
15
- require 'spec/lib/helpers'
16
-
17
- require 'yaml'
18
- require 'ldap'
19
- require 'ldap/schema'
20
- require 'treequel/schema/objectclass'
21
- require 'treequel/schema/attributetype'
22
- rescue LoadError
23
- unless Object.const_defined?( :Gem )
24
- require 'rubygems'
25
- retry
26
- end
27
- raise
28
- end
12
+ require 'spec'
13
+ require 'spec/lib/constants'
14
+ require 'spec/lib/helpers'
29
15
 
16
+ require 'yaml'
17
+ require 'ldap'
18
+ require 'ldap/schema'
19
+ require 'treequel/schema/objectclass'
20
+ require 'treequel/schema/attributetype'
30
21
 
31
22
  include Treequel::TestConstants
32
23
  include Treequel::Constants
@@ -38,14 +29,40 @@ include Treequel::Constants
38
29
  describe Treequel::Schema::ObjectClass do
39
30
  include Treequel::SpecHelpers
40
31
 
32
+ TOP_OBJECTCLASS =
33
+ %{( 2.5.6.0 NAME 'top' } +
34
+ %{DESC 'top of the superclass chain' ABSTRACT } +
35
+ %{MUST objectClass )}
36
+
37
+ PERSON_OBJECTCLASS =
38
+ %{( 2.5.6.6 NAME 'person'} +
39
+ %{ DESC 'RFC2256: a person'} +
40
+ %{ SUP top STRUCTURAL} +
41
+ %{ MUST ( sn $ cn )} +
42
+ %{ MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )}
43
+
44
+ ORGPERSON_OBJECTCLASS =
45
+ %{( 2.5.6.7 NAME 'organizationalPerson'} +
46
+ %{ DESC 'RFC2256: an organizational person'} +
47
+ %{ SUP person STRUCTURAL} +
48
+ %{ MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $} +
49
+ %{ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $} +
50
+ %{ telephoneNumber $ internationaliSDNNumber $ } +
51
+ %{ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $} +
52
+ %{ postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) )}
53
+
41
54
 
42
55
  before( :all ) do
43
- setup_logging( :fatal )
56
+ setup_logging( :debug )
44
57
  @datadir = Pathname( __FILE__ ).dirname.parent.parent + 'data'
45
58
  end
46
59
 
47
60
  before( :each ) do
48
- @schema = mock( "Treequel schema" )
61
+ octable = {}
62
+ @schema = stub( "Treequel schema", :object_classes => octable )
63
+ octable[:top] = Treequel::Schema::ObjectClass.parse( @schema, TOP_OBJECTCLASS )
64
+ octable[:person] = Treequel::Schema::ObjectClass.parse( @schema, PERSON_OBJECTCLASS )
65
+ octable[:orgPerson] = Treequel::Schema::ObjectClass.parse( @schema, ORGPERSON_OBJECTCLASS )
49
66
  end
50
67
 
51
68
  after( :all ) do
@@ -55,11 +72,8 @@ describe Treequel::Schema::ObjectClass do
55
72
 
56
73
  describe "parsed from the 'top' objectClass" do
57
74
 
58
- TOP_OBJECTCLASS = %{( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRACT } +
59
- %{MUST objectClass )}
60
-
61
75
  before( :each ) do
62
- @oc = Treequel::Schema::ObjectClass.parse( @schema, TOP_OBJECTCLASS )
76
+ @oc = @schema.object_classes[:top]
63
77
  end
64
78
 
65
79
  it "is an AbstractObjectClass because its 'kind' is 'ABSTRACT'" do
@@ -114,19 +128,10 @@ describe Treequel::Schema::ObjectClass do
114
128
  end
115
129
 
116
130
 
117
- describe "parsed from the 'organizationalUnit' objectClass" do
118
-
119
- OU_OBJECTCLASS = %{( 2.5.6.5 NAME 'organizationalUnit' } +
120
- %{DESC 'RFC2256: an organizational unit' SUP top STRUCTURAL } +
121
- %{MUST ou MAY ( userPassword $ searchGuide $ seeAlso $ } +
122
- %{businessCategory $ x121Address $ registeredAddress $ } +
123
- %{destinationIndicator $ preferredDeliveryMethod $ telexNumber $ } +
124
- %{teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ } +
125
- %{facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ } +
126
- %{physicalDeliveryOfficeName $ st $ l $ description ) )}
131
+ describe "parsed from the 'organizationalPerson' objectClass" do
127
132
 
128
133
  before( :each ) do
129
- @oc = Treequel::Schema::ObjectClass.parse( @schema, OU_OBJECTCLASS )
134
+ @oc = @schema.object_classes[:orgPerson]
130
135
  end
131
136
 
132
137
  it "is a StructuralObjectClass because its kind is 'STRUCTURAL'" do
@@ -134,29 +139,45 @@ describe Treequel::Schema::ObjectClass do
134
139
  end
135
140
 
136
141
  it "knows what OID corresponds to the class" do
137
- @oc.oid.should == '2.5.6.5'
142
+ @oc.oid.should == '2.5.6.7'
138
143
  end
139
144
 
140
145
  it "knows what its NAME attribute is" do
141
- @oc.name.should == :organizationalUnit
146
+ @oc.name.should == :organizationalPerson
142
147
  end
143
148
 
144
149
  it "knows what its DESC attribute is" do
145
- @oc.desc.should == 'RFC2256: an organizational unit'
150
+ @oc.desc.should == 'RFC2256: an organizational person'
146
151
  end
147
152
 
148
- it "knows that it has one MUST attribute" do
149
- @oc.must_oids.should have( 1 ).member
150
- @oc.must_oids.should == [ :ou ]
153
+ it "knows what its MUST attributes are" do
154
+ @oc.must_oids.should have( 3 ).members
155
+ @oc.must_oids.should include( :sn, :cn, :objectClass )
156
+ end
157
+
158
+ it "knows what its unique MUST attributes are" do
159
+ @oc.must_oids( false ).should be_empty()
151
160
  end
152
161
 
153
162
  it "knows what its MAY attributes are" do
154
- @oc.may_oids.should have( 21 ).members
155
- @oc.may_oids.should include( :userPassword, :searchGuide, :seeAlso, :businessCategory,
156
- :x121Address, :registeredAddress, :destinationIndicator, :preferredDeliveryMethod,
157
- :telexNumber, :teletexTerminalIdentifier, :telephoneNumber, :internationaliSDNNumber,
158
- :facsimileTelephoneNumber, :street, :postOfficeBox, :postalCode, :postalAddress,
159
- :physicalDeliveryOfficeName, :st, :l, :description )
163
+ @oc.may_oids.should have( 22 ).members
164
+ @oc.may_oids.should include(
165
+ :userPassword, :telephoneNumber, :seeAlso, :description,
166
+ :title, :x121Address, :registeredAddress, :destinationIndicator,
167
+ :preferredDeliveryMethod, :telexNumber, :teletexTerminalIdentifier,
168
+ :telephoneNumber, :internationaliSDNNumber, :facsimileTelephoneNumber,
169
+ :street, :postOfficeBox, :postalCode, :postalAddress,
170
+ :physicalDeliveryOfficeName, :ou, :st, :l )
171
+ end
172
+
173
+ it "knows what its unique MAY attributes are" do
174
+ @oc.may_oids( false ).should have( 18 ).members
175
+ @oc.may_oids( false ).should include(
176
+ :title, :x121Address, :registeredAddress, :destinationIndicator,
177
+ :preferredDeliveryMethod, :telexNumber, :teletexTerminalIdentifier,
178
+ :telephoneNumber, :internationaliSDNNumber, :facsimileTelephoneNumber,
179
+ :street, :postOfficeBox, :postalCode, :postalAddress,
180
+ :physicalDeliveryOfficeName, :ou, :st, :l )
160
181
  end
161
182
 
162
183
  end
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+ begin
13
+ require 'spec'
14
+ require 'spec/lib/constants'
15
+ require 'spec/lib/helpers'
16
+
17
+ require 'yaml'
18
+ require 'ldap'
19
+ require 'ldap/schema'
20
+ require 'treequel/schema'
21
+ rescue LoadError
22
+ unless Object.const_defined?( :Gem )
23
+ require 'rubygems'
24
+ retry
25
+ end
26
+ raise
27
+ end
28
+
29
+
30
+ include Treequel::TestConstants
31
+ include Treequel::Constants
32
+
33
+ #####################################################################
34
+ ### C O N T E X T S
35
+ #####################################################################
36
+
37
+ describe Treequel::Schema::Table do
38
+ include Treequel::SpecHelpers
39
+
40
+
41
+ before( :all ) do
42
+ setup_logging( :fatal )
43
+ end
44
+
45
+ before( :each ) do
46
+ @table = Treequel::Schema::Table.new
47
+ end
48
+
49
+ after( :all ) do
50
+ reset_logging()
51
+ end
52
+
53
+
54
+ it "allows setting/fetching case-insensitively" do
55
+ @table['organizationalRole'] = :or
56
+ @table["apple-preset-computer-list"] = :applepreset
57
+ @table.deltaCRL = :deltacrl
58
+
59
+ @table['organizationalrole'].should == :or
60
+ @table[:organizationalrole].should == :or
61
+ @table.organizationalRole.should == :or
62
+ @table.organizationalrole.should == :or
63
+
64
+ @table[:"apple-preset-computer-list"].should == :applepreset
65
+ @table['apple-preset-computer-list'].should == :applepreset
66
+ @table[:apple_preset_computer_list].should == :applepreset
67
+ @table.apple_preset_computer_list.should == :applepreset
68
+
69
+ @table['deltacrl'].should == :deltacrl
70
+ @table[:deltaCRL].should == :deltacrl
71
+ @table[:deltacrl].should == :deltacrl
72
+ @table.deltaCRL.should == :deltacrl
73
+ @table.deltacrl.should == :deltacrl
74
+
75
+ end
76
+
77
+
78
+ it "doesn't try to normalize numeric OIDs" do
79
+ @table['1.3.6.1.4.1.4203.666.11.1.4.2.1.2'] = :an_oid
80
+ @table['1.3.6.1.4.1.4203.666.11.1.4.2.1.2'].should == :an_oid
81
+ @table['13614142036661114212'].should_not == :an_oid
82
+ @table.keys.should include( '1.3.6.1.4.1.4203.666.11.1.4.2.1.2' )
83
+ end
84
+
85
+
86
+ it "merges other Tables" do
87
+ othertable = Treequel::Schema::Table.new
88
+
89
+ @table['ou'] = 'thing'
90
+ @table['cn'] = 'chunker'
91
+
92
+ othertable['cn'] = 'phunker'
93
+
94
+ ot = @table.merge( othertable )
95
+ ot['ou'].should == 'thing'
96
+ ot['cn'].should == 'phunker'
97
+ end
98
+
99
+
100
+ it "merges hashes after normalizing keys" do
101
+ @table['ou'] = 'thing'
102
+ @table['apple-computer-list'] = 'trishtrash'
103
+
104
+ hash = { 'apple-computer-list' => 'pinhash' }
105
+
106
+ ot = @table.merge( hash )
107
+ ot['ou'].should == 'thing'
108
+ ot['apple-computer-list'].should == 'pinhash'
109
+ end
110
+
111
+
112
+ it "dupes its inner hash when duped" do
113
+ newtable = @table.dup
114
+
115
+ newtable[:cn] = 'god'
116
+ @table.should_not include( :cn )
117
+ @table.should be_empty()
118
+ end
119
+
120
+
121
+ it "provides a case-insensitive version of #values_at" do
122
+ @table[:cn] = 'contra_rules'
123
+ @table[:d] = 'ghosty'
124
+ @table[:porntipsGuzzardo] = 'cha-ching'
125
+
126
+ results = @table.values_at( :CN, 'PornTipsGuzzARDO' )
127
+ results.should include( 'contra_rules' )
128
+ results.should include( 'cha-ching' )
129
+ results.should_not include( 'ghosty' )
130
+ end
131
+
132
+ end
133
+
134
+
@@ -9,21 +9,12 @@ BEGIN {
9
9
  $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
10
  }
11
11
 
12
- begin
13
- require 'spec'
14
- require 'spec/lib/constants'
15
- require 'spec/lib/helpers'
16
-
17
- require 'treequel'
18
- require 'treequel/directory'
19
- rescue LoadError
20
- unless Object.const_defined?( :Gem )
21
- require 'rubygems'
22
- retry
23
- end
24
- raise
25
- end
12
+ require 'spec'
13
+ require 'spec/lib/constants'
14
+ require 'spec/lib/helpers'
26
15
 
16
+ require 'treequel'
17
+ require 'treequel/directory'
27
18
 
28
19
  include Treequel::TestConstants
29
20
 
@@ -36,6 +27,10 @@ describe Treequel do
36
27
  include Treequel::SpecHelpers
37
28
 
38
29
  before( :all ) do
30
+ setup_logging( :fatal )
31
+ end
32
+
33
+ after( :all ) do
39
34
  reset_logging()
40
35
  end
41
36
 
@@ -53,7 +48,7 @@ describe Treequel do
53
48
 
54
49
 
55
50
  it "returns a version string with a build number if asked" do
56
- Treequel.version_string(true).should =~ /\w+ [\d.]+ \(build rev.*\)/
51
+ Treequel.version_string(true).should =~ /\w+ [\d.]+ \(build [[:xdigit:]]+\)/
57
52
  end
58
53
 
59
54
 
@@ -126,6 +121,273 @@ describe Treequel do
126
121
  end
127
122
 
128
123
 
124
+ it "provides a convenience method for creating directory objects from the system LDAP config" do
125
+ Treequel.should_receive( :find_configfile ).and_return( :a_configfile_path )
126
+ Treequel.should_receive( :read_opts_from_config ).with( :a_configfile_path ).
127
+ and_return({ :configfile_opts => 1 })
128
+ Treequel.should_receive( :read_opts_from_environment ).
129
+ and_return({ :environment_opts => 1 })
130
+
131
+ merged_config = Treequel::Directory::DEFAULT_OPTIONS.
132
+ merge({ :configfile_opts => 1, :environment_opts => 1 })
133
+
134
+ Treequel::Directory.should_receive( :new ).with( merged_config ).
135
+ and_return( :a_directory )
136
+
137
+ Treequel.directory_from_config.should == :a_directory
138
+ end
139
+
140
+
141
+ describe "system LDAP config methods" do
142
+
143
+ before( :each ) do
144
+ ENV['LDAPCONF'] = nil
145
+ ENV['LDAPRC'] = nil
146
+ ENV['LDAPURI'] = nil
147
+ ENV['LDAPBASE'] = nil
148
+ ENV['LDAPBINDDN'] = nil
149
+ ENV['LDAPHOST'] = nil
150
+ ENV['LDAPPORT'] = nil
151
+ end
152
+
153
+
154
+ it "uses the LDAPCONF environment variable if it is set" do
155
+ configpath = mock( "configfile Pathname object" )
156
+ ENV['LDAPCONF'] = 'a_configfile_path'
157
+
158
+ Treequel.should_receive( :Pathname ).with( 'a_configfile_path' ).and_return( configpath )
159
+ configpath.should_receive( :expand_path ).and_return( configpath )
160
+ configpath.should_receive( :readable? ).and_return( true )
161
+
162
+ Treequel.find_configfile.should == configpath
163
+ end
164
+
165
+ it "raises an exception if the file specified in LDAPCONF isn't readable" do
166
+ configpath = mock( "configfile Pathname object" )
167
+ ENV['LDAPCONF'] = 'a_configfile_path'
168
+
169
+ Treequel.should_receive( :Pathname ).with( 'a_configfile_path' ).and_return( configpath )
170
+ configpath.should_receive( :expand_path ).and_return( configpath )
171
+ configpath.should_receive( :readable? ).and_return( false )
172
+
173
+ expect {
174
+ Treequel.find_configfile
175
+ }.to raise_exception( RuntimeError, /a_configfile_path.*LDAPCONF/ )
176
+ end
177
+
178
+ it "uses the LDAPRC environment variable if it is set and LDAPCONF isn't" do
179
+ configpath = mock( "configfile Pathname object" )
180
+ ENV['LDAPRC'] = 'a_configfile_path'
181
+
182
+ Treequel.should_receive( :Pathname ).with( 'a_configfile_path' ).and_return( configpath )
183
+ configpath.should_receive( :expand_path ).and_return( configpath )
184
+ configpath.should_receive( :readable? ).and_return( true )
185
+
186
+ Treequel.find_configfile.should == configpath
187
+ end
188
+
189
+ it "looks in the current user's HOME for the LDAPRC file if it isn't in the CWD" do
190
+ cwd_path = mock( "CWD configfile Pathname object" )
191
+ homedir_path = mock( "HOME configfile Pathname object" )
192
+ ENV['LDAPRC'] = 'a_configfile_path'
193
+
194
+ Treequel.should_receive( :Pathname ).with( 'a_configfile_path' ).and_return( cwd_path )
195
+ cwd_path.should_receive( :expand_path ).and_return( cwd_path )
196
+ cwd_path.should_receive( :readable? ).and_return( false )
197
+
198
+ Treequel.should_receive( :Pathname ).with( '~' ).and_return( homedir_path )
199
+ homedir_path.should_receive( :expand_path ).and_return( homedir_path )
200
+ homedir_path.should_receive( :+ ).with( 'a_configfile_path' ).and_return( homedir_path )
201
+ homedir_path.should_receive( :readable? ).and_return( true )
202
+
203
+ Treequel.find_configfile.should == homedir_path
204
+ end
205
+
206
+ it "raises an exception if the file specified in LDAPRC isn't readable" do
207
+ cwd_path = mock( "CWD configfile Pathname object" )
208
+ homedir_path = mock( "HOME configfile Pathname object" )
209
+ ENV['LDAPRC'] = 'a_configfile_path'
210
+
211
+ Treequel.should_receive( :Pathname ).with( 'a_configfile_path' ).and_return( cwd_path )
212
+ cwd_path.should_receive( :expand_path ).and_return( cwd_path )
213
+ cwd_path.should_receive( :readable? ).and_return( false )
214
+
215
+ Treequel.should_receive( :Pathname ).with( '~' ).and_return( homedir_path )
216
+ homedir_path.should_receive( :expand_path ).and_return( homedir_path )
217
+ homedir_path.should_receive( :+ ).with( 'a_configfile_path' ).and_return( homedir_path )
218
+ homedir_path.should_receive( :readable? ).and_return( false )
219
+
220
+ expect {
221
+ Treequel.find_configfile
222
+ }.to raise_exception( RuntimeError, /a_configfile_path.*LDAPRC/ )
223
+ end
224
+
225
+ it "searches a list of common ldap.conf paths if neither LDAPCONF nor LDAPRC are set" do
226
+ pathmocks = []
227
+
228
+ Treequel::COMMON_LDAP_CONF_PATHS.each do |path|
229
+ pathname = mock( "pathname: #{path}" )
230
+ pathmocks << pathname
231
+ end
232
+ Treequel.should_receive( :Pathname ).and_return( *pathmocks )
233
+
234
+ # Index of the entry we're going to pretend exists
235
+ successful_index = 6
236
+ 0.upto( successful_index ) do |i|
237
+ pathmocks[i].should_receive( :readable? ).and_return( i == successful_index )
238
+ end
239
+
240
+ Treequel.find_configfile.should == pathmocks[ successful_index ]
241
+ end
242
+
243
+ #
244
+ # OpenLDAP-style config
245
+ #
246
+
247
+ it "maps the OpenLDAP URI directive to equivalent options" do
248
+ IO.should_receive( :foreach ).with( :a_configfile ).
249
+ and_yield( "URI ldap://ldap.acme.com/dc=acme,dc=com" )
250
+ Treequel.read_opts_from_config( :a_configfile ).should ==
251
+ { :port => 389, :base_dn => "dc=acme,dc=com", :host => "ldap.acme.com" }
252
+ end
253
+
254
+ it "maps the OpenLDAP BASE directive to the base_dn option" do
255
+ IO.should_receive( :foreach ).with( :a_configfile ).
256
+ and_yield( "BASE dc=acme,dc=com" )
257
+ Treequel.read_opts_from_config( :a_configfile ).should ==
258
+ { :base_dn => "dc=acme,dc=com" }
259
+ end
260
+
261
+ it "maps the OpenLDAP BINDDN directive to the bind_dn option" do
262
+ IO.should_receive( :foreach ).with( :a_configfile ).
263
+ and_yield( "BINDDN cn=admin,dc=acme,dc=com" )
264
+ Treequel.read_opts_from_config( :a_configfile ).should ==
265
+ { :bind_dn => "cn=admin,dc=acme,dc=com" }
266
+ end
267
+
268
+ it "maps the OpenLDAP HOST directive to the host option" do
269
+ IO.should_receive( :foreach ).with( :a_configfile ).
270
+ and_yield( "# Host file\nHOST ldap.acme.com\n\n" )
271
+ Treequel.read_opts_from_config( :a_configfile ).should ==
272
+ { :host => 'ldap.acme.com' }
273
+ end
274
+
275
+ it "maps the OpenLDAP PORT directive to the port option" do
276
+ IO.should_receive( :foreach ).with( :a_configfile ).
277
+ and_yield( "PORT 389" )
278
+ Treequel.read_opts_from_config( :a_configfile ).should ==
279
+ { :port => 389 }
280
+ end
281
+
282
+ #
283
+ # NSS-style config
284
+ #
285
+
286
+ it "maps the nss-style uri directive to equivalent options" do
287
+ IO.should_receive( :foreach ).with( :a_configfile ).
288
+ and_yield( "uri ldap://ldap.acme.com/dc=acme,dc=com" )
289
+ Treequel.read_opts_from_config( :a_configfile ).should ==
290
+ { :port => 389, :base_dn => "dc=acme,dc=com", :host => "ldap.acme.com" }
291
+ end
292
+
293
+ it "maps the nss-style 'host' option correctly" do
294
+ IO.should_receive( :foreach ).with( :a_configfile ).
295
+ and_yield( "host ldap.acme.com\n\n" )
296
+ Treequel.read_opts_from_config( :a_configfile ).should ==
297
+ { :host => 'ldap.acme.com' }
298
+ end
299
+
300
+ it "maps the nss-style 'binddn' option correctly" do
301
+ IO.should_receive( :foreach ).with( :a_configfile ).
302
+ and_yield( "binddn cn=superuser,dc=acme,dc=com" )
303
+ Treequel.read_opts_from_config( :a_configfile ).should ==
304
+ { :bind_dn => "cn=superuser,dc=acme,dc=com" }
305
+ end
306
+
307
+ it "maps the nss-style 'bindpw' option correctly" do
308
+ IO.should_receive( :foreach ).with( :a_configfile ).
309
+ and_yield( "# My totally awesome password" ).
310
+ and_yield( "bindpw a:password!" )
311
+ Treequel.read_opts_from_config( :a_configfile ).should ==
312
+ { :pass => "a:password!" }
313
+ end
314
+
315
+ it "maps the nss-style 'base' option correctly" do
316
+ IO.should_receive( :foreach ).with( :a_configfile ).
317
+ and_yield( "base dc=acme,dc=com" )
318
+ Treequel.read_opts_from_config( :a_configfile ).should ==
319
+ { :base_dn => "dc=acme,dc=com" }
320
+ end
321
+
322
+ it "maps the nss-style 'ssl' option to the correct port and connect_type if it's 'off'" do
323
+ IO.should_receive( :foreach ).with( :a_configfile ).
324
+ and_yield( "ssl off" )
325
+ Treequel.read_opts_from_config( :a_configfile ).should ==
326
+ { :port => 389, :connect_type => :plain }
327
+ end
328
+
329
+ it "maps the nss-style 'ssl' option to the correct port and connect_type if " +
330
+ "it's 'start_tls'" do
331
+ IO.should_receive( :foreach ).with( :a_configfile ).
332
+ and_yield( '' ).
333
+ and_yield( '# Use TLS' ).
334
+ and_yield( 'ssl start_tls' )
335
+ Treequel.read_opts_from_config( :a_configfile ).should ==
336
+ { :port => 389, :connect_type => :tls }
337
+ end
338
+
339
+ it "maps the nss-style 'ssl' option to the correct port and connect_type if " +
340
+ "it's 'on'" do
341
+ IO.should_receive( :foreach ).with( :a_configfile ).
342
+ and_yield( "\n# Use plain SSL\nssl on\n" )
343
+ Treequel.read_opts_from_config( :a_configfile ).should ==
344
+ { :port => 636, :connect_type => :ssl }
345
+ end
346
+
347
+ #
348
+ # Environment
349
+ #
350
+
351
+ it "maps the OpenLDAP LDAPURI environment variable to equivalent options" do
352
+ ENV['LDAPURI'] = 'ldaps://quomsohutch.linkerlinlinkin.org/o=linkerlickin'
353
+ Treequel.read_opts_from_environment.should == {
354
+ :host => 'quomsohutch.linkerlinlinkin.org',
355
+ :connect_type => :ssl,
356
+ :port => 636,
357
+ :base_dn => 'o=linkerlickin'
358
+ }
359
+ end
360
+
361
+ it "maps the OpenLDAP LDAPBASE environment variable to the base_dn option" do
362
+ ENV['LDAPBASE'] = 'o=linkerlickin'
363
+ Treequel.read_opts_from_environment.should == {
364
+ :base_dn => 'o=linkerlickin'
365
+ }
366
+ end
367
+
368
+ it "maps the OpenLDAP LDAPBINDDN environment variable to the bind_dn option" do
369
+ ENV['LDAPBINDDN'] = 'cn=superuser,ou=people,o=linkerlickin'
370
+ Treequel.read_opts_from_environment.should == {
371
+ :bind_dn => 'cn=superuser,ou=people,o=linkerlickin'
372
+ }
373
+ end
374
+
375
+ it "maps the OpenLDAP LDAPHOST environment variable to the host option" do
376
+ ENV['LDAPHOST'] = 'quomsohutch.linkerlinlinkin.org'
377
+ Treequel.read_opts_from_environment.should == {
378
+ :host => 'quomsohutch.linkerlinlinkin.org',
379
+ }
380
+ end
381
+
382
+ it "maps the OpenLDAP LDAPPORT environment variable to the port option" do
383
+ ENV['LDAPPORT'] = '636'
384
+ Treequel.read_opts_from_environment.should == {
385
+ :port => 636,
386
+ }
387
+ end
388
+ end
389
+
390
+
129
391
  describe " logging subsystem" do
130
392
  before(:each) do
131
393
  Treequel.reset_logger