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