treequel 1.0.1 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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