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.
- data/ChangeLog +176 -14
- data/LICENSE +1 -1
- data/Rakefile +61 -45
- data/Rakefile.local +20 -0
- data/bin/treequel +502 -269
- data/examples/ldap-rack-auth.rb +2 -0
- data/lib/treequel.rb +221 -18
- data/lib/treequel/branch.rb +410 -201
- data/lib/treequel/branchcollection.rb +25 -13
- data/lib/treequel/branchset.rb +42 -40
- data/lib/treequel/constants.rb +233 -3
- data/lib/treequel/control.rb +95 -0
- data/lib/treequel/controls/contentsync.rb +138 -0
- data/lib/treequel/controls/pagedresults.rb +162 -0
- data/lib/treequel/controls/sortedresults.rb +216 -0
- data/lib/treequel/directory.rb +212 -65
- data/lib/treequel/exceptions.rb +11 -12
- data/lib/treequel/filter.rb +1 -12
- data/lib/treequel/mixins.rb +83 -47
- data/lib/treequel/monkeypatches.rb +29 -0
- data/lib/treequel/schema.rb +23 -19
- data/lib/treequel/schema/attributetype.rb +33 -3
- data/lib/treequel/schema/ldapsyntax.rb +0 -11
- data/lib/treequel/schema/matchingrule.rb +0 -11
- data/lib/treequel/schema/matchingruleuse.rb +0 -11
- data/lib/treequel/schema/objectclass.rb +36 -10
- data/lib/treequel/schema/table.rb +159 -0
- data/lib/treequel/sequel_integration.rb +7 -7
- data/lib/treequel/utils.rb +4 -66
- data/rake/documentation.rb +89 -0
- data/rake/helpers.rb +375 -307
- data/rake/hg.rb +16 -2
- data/rake/manual.rb +11 -6
- data/rake/packaging.rb +20 -35
- data/rake/publishing.rb +22 -62
- data/spec/lib/constants.rb +20 -0
- data/spec/lib/control_behavior.rb +44 -0
- data/spec/lib/matchers.rb +51 -0
- data/spec/treequel/branch_spec.rb +88 -29
- data/spec/treequel/branchcollection_spec.rb +24 -1
- data/spec/treequel/branchset_spec.rb +123 -51
- data/spec/treequel/control_spec.rb +48 -0
- data/spec/treequel/controls/contentsync_spec.rb +38 -0
- data/spec/treequel/controls/pagedresults_spec.rb +138 -0
- data/spec/treequel/controls/sortedresults_spec.rb +171 -0
- data/spec/treequel/directory_spec.rb +186 -16
- data/spec/treequel/mixins_spec.rb +42 -3
- data/spec/treequel/schema/attributetype_spec.rb +22 -20
- data/spec/treequel/schema/objectclass_spec.rb +67 -46
- data/spec/treequel/schema/table_spec.rb +134 -0
- data/spec/treequel_spec.rb +277 -15
- metadata +89 -108
- data/bin/treequel.orig +0 -963
- data/examples/ldap-monitor.rb +0 -143
- data/examples/ldap-monitor/public/css/master.css +0 -328
- data/examples/ldap-monitor/public/images/card_small.png +0 -0
- data/examples/ldap-monitor/public/images/chain_small.png +0 -0
- data/examples/ldap-monitor/public/images/globe_small.png +0 -0
- data/examples/ldap-monitor/public/images/globe_small_green.png +0 -0
- data/examples/ldap-monitor/public/images/plug.png +0 -0
- data/examples/ldap-monitor/public/images/shadows/large-30-down.png +0 -0
- data/examples/ldap-monitor/public/images/tick.png +0 -0
- data/examples/ldap-monitor/public/images/tick_circle.png +0 -0
- data/examples/ldap-monitor/public/images/treequel-favicon.png +0 -0
- data/examples/ldap-monitor/views/backends.erb +0 -41
- data/examples/ldap-monitor/views/connections.erb +0 -74
- data/examples/ldap-monitor/views/databases.erb +0 -39
- data/examples/ldap-monitor/views/dump_subsystem.erb +0 -14
- data/examples/ldap-monitor/views/index.erb +0 -14
- data/examples/ldap-monitor/views/layout.erb +0 -35
- data/examples/ldap-monitor/views/listeners.erb +0 -30
- data/rake/rdoc.rb +0 -30
- 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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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 ('
|
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( :
|
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 == :
|
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
|
-
|
13
|
-
|
14
|
-
|
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( :
|
56
|
+
setup_logging( :debug )
|
44
57
|
@datadir = Pathname( __FILE__ ).dirname.parent.parent + 'data'
|
45
58
|
end
|
46
59
|
|
47
60
|
before( :each ) do
|
48
|
-
|
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 =
|
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 '
|
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 =
|
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.
|
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 == :
|
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
|
150
|
+
@oc.desc.should == 'RFC2256: an organizational person'
|
146
151
|
end
|
147
152
|
|
148
|
-
it "knows
|
149
|
-
@oc.must_oids.should have(
|
150
|
-
@oc.must_oids.should
|
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(
|
155
|
-
@oc.may_oids.should include(
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
+
|
data/spec/treequel_spec.rb
CHANGED
@@ -9,21 +9,12 @@ BEGIN {
|
|
9
9
|
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
10
10
|
}
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
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
|