vines 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +19 -0
- data/README +34 -0
- data/Rakefile +55 -0
- data/bin/vines +95 -0
- data/conf/certs/README +32 -0
- data/conf/certs/ca-bundle.crt +3987 -0
- data/conf/config.rb +114 -0
- data/lib/vines.rb +155 -0
- data/lib/vines/command/bcrypt.rb +12 -0
- data/lib/vines/command/cert.rb +49 -0
- data/lib/vines/command/init.rb +58 -0
- data/lib/vines/command/ldap.rb +35 -0
- data/lib/vines/command/restart.rb +12 -0
- data/lib/vines/command/schema.rb +24 -0
- data/lib/vines/command/start.rb +28 -0
- data/lib/vines/command/stop.rb +18 -0
- data/lib/vines/config.rb +191 -0
- data/lib/vines/contact.rb +99 -0
- data/lib/vines/daemon.rb +78 -0
- data/lib/vines/error.rb +150 -0
- data/lib/vines/jid.rb +56 -0
- data/lib/vines/kit.rb +23 -0
- data/lib/vines/router.rb +125 -0
- data/lib/vines/stanza.rb +55 -0
- data/lib/vines/stanza/iq.rb +50 -0
- data/lib/vines/stanza/iq/auth.rb +18 -0
- data/lib/vines/stanza/iq/disco_info.rb +25 -0
- data/lib/vines/stanza/iq/disco_items.rb +23 -0
- data/lib/vines/stanza/iq/error.rb +16 -0
- data/lib/vines/stanza/iq/ping.rb +16 -0
- data/lib/vines/stanza/iq/query.rb +10 -0
- data/lib/vines/stanza/iq/result.rb +16 -0
- data/lib/vines/stanza/iq/roster.rb +153 -0
- data/lib/vines/stanza/iq/session.rb +22 -0
- data/lib/vines/stanza/iq/vcard.rb +58 -0
- data/lib/vines/stanza/message.rb +41 -0
- data/lib/vines/stanza/presence.rb +119 -0
- data/lib/vines/stanza/presence/error.rb +23 -0
- data/lib/vines/stanza/presence/probe.rb +38 -0
- data/lib/vines/stanza/presence/subscribe.rb +66 -0
- data/lib/vines/stanza/presence/subscribed.rb +64 -0
- data/lib/vines/stanza/presence/unavailable.rb +15 -0
- data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
- data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
- data/lib/vines/storage.rb +216 -0
- data/lib/vines/storage/couchdb.rb +119 -0
- data/lib/vines/storage/ldap.rb +59 -0
- data/lib/vines/storage/local.rb +66 -0
- data/lib/vines/storage/redis.rb +108 -0
- data/lib/vines/storage/sql.rb +174 -0
- data/lib/vines/store.rb +51 -0
- data/lib/vines/stream.rb +198 -0
- data/lib/vines/stream/client.rb +131 -0
- data/lib/vines/stream/client/auth.rb +94 -0
- data/lib/vines/stream/client/auth_restart.rb +33 -0
- data/lib/vines/stream/client/bind.rb +58 -0
- data/lib/vines/stream/client/bind_restart.rb +25 -0
- data/lib/vines/stream/client/closed.rb +13 -0
- data/lib/vines/stream/client/ready.rb +15 -0
- data/lib/vines/stream/client/start.rb +27 -0
- data/lib/vines/stream/client/tls.rb +37 -0
- data/lib/vines/stream/component.rb +53 -0
- data/lib/vines/stream/component/handshake.rb +25 -0
- data/lib/vines/stream/component/ready.rb +24 -0
- data/lib/vines/stream/component/start.rb +19 -0
- data/lib/vines/stream/http.rb +111 -0
- data/lib/vines/stream/http/http_request.rb +22 -0
- data/lib/vines/stream/http/http_state.rb +139 -0
- data/lib/vines/stream/http/http_states.rb +53 -0
- data/lib/vines/stream/parser.rb +78 -0
- data/lib/vines/stream/server.rb +126 -0
- data/lib/vines/stream/server/auth.rb +13 -0
- data/lib/vines/stream/server/auth_restart.rb +19 -0
- data/lib/vines/stream/server/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth.rb +31 -0
- data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
- data/lib/vines/stream/server/outbound/final_features.rb +27 -0
- data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/start.rb +20 -0
- data/lib/vines/stream/server/outbound/tls.rb +30 -0
- data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
- data/lib/vines/stream/server/ready.rb +20 -0
- data/lib/vines/stream/server/start.rb +13 -0
- data/lib/vines/stream/server/tls.rb +13 -0
- data/lib/vines/stream/state.rb +55 -0
- data/lib/vines/token_bucket.rb +46 -0
- data/lib/vines/user.rb +124 -0
- data/lib/vines/version.rb +5 -0
- data/lib/vines/xmpp_server.rb +25 -0
- data/test/config_test.rb +396 -0
- data/test/error_test.rb +59 -0
- data/test/ext/nokogiri.rb +14 -0
- data/test/jid_test.rb +71 -0
- data/test/kit_test.rb +21 -0
- data/test/router_test.rb +60 -0
- data/test/stanza/iq/roster_test.rb +198 -0
- data/test/stanza/iq/session_test.rb +30 -0
- data/test/stanza/iq/vcard_test.rb +159 -0
- data/test/stanza/message_test.rb +124 -0
- data/test/stanza/presence/subscribe_test.rb +75 -0
- data/test/storage/couchdb_test.rb +102 -0
- data/test/storage/ldap_test.rb +207 -0
- data/test/storage/local_test.rb +54 -0
- data/test/storage/redis_test.rb +75 -0
- data/test/storage/sql_test.rb +55 -0
- data/test/storage/storage_tests.rb +134 -0
- data/test/storage_test.rb +90 -0
- data/test/stream/client/auth_test.rb +127 -0
- data/test/stream/client/ready_test.rb +47 -0
- data/test/stream/component/handshake_test.rb +46 -0
- data/test/stream/component/ready_test.rb +105 -0
- data/test/stream/component/start_test.rb +41 -0
- data/test/stream/parser_test.rb +121 -0
- data/test/stream/server/outbound/auth_test.rb +77 -0
- data/test/stream/server/ready_test.rb +100 -0
- data/test/token_bucket_test.rb +24 -0
- data/test/user_test.rb +64 -0
- metadata +318 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'vines'
|
|
4
|
+
require 'ext/nokogiri'
|
|
5
|
+
require 'minitest/mock'
|
|
6
|
+
require 'test/unit'
|
|
7
|
+
|
|
8
|
+
class MessageTest < Test::Unit::TestCase
|
|
9
|
+
def setup
|
|
10
|
+
@stream = MiniTest::Mock.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_bad_type_returns_error
|
|
14
|
+
node = node('<message type="bogus">hello!</message>')
|
|
15
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
|
16
|
+
assert_raise(Vines::StanzaErrors::BadRequest) { stanza.process }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_missing_to_address_is_sent_to_sender
|
|
20
|
+
alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
|
|
21
|
+
node = node('<message>hello!</message>')
|
|
22
|
+
|
|
23
|
+
recipient = MiniTest::Mock.new
|
|
24
|
+
router = MiniTest::Mock.new
|
|
25
|
+
router.expect(:local?, true, [node])
|
|
26
|
+
router.expect(:connected_resources, [recipient], [alice.jid.bare])
|
|
27
|
+
|
|
28
|
+
@stream.expect(:router, router)
|
|
29
|
+
@stream.expect(:user, alice)
|
|
30
|
+
@stream.expect(:broadcast, nil, [node, [recipient]])
|
|
31
|
+
|
|
32
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
|
33
|
+
assert_nothing_raised { stanza.process }
|
|
34
|
+
assert @stream.verify
|
|
35
|
+
assert router.verify
|
|
36
|
+
assert recipient.verify
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_message_to_non_user_is_ignored
|
|
40
|
+
bogus = Vines::JID.new('bogus@wonderland.lit/cake')
|
|
41
|
+
node = node(%Q{<message to="#{bogus}">hello!</message>})
|
|
42
|
+
|
|
43
|
+
router = MiniTest::Mock.new
|
|
44
|
+
router.expect(:local?, true, [node])
|
|
45
|
+
router.expect(:connected_resources, [], [bogus])
|
|
46
|
+
|
|
47
|
+
storage = MiniTest::Mock.new
|
|
48
|
+
storage.expect(:find_user, nil, [bogus])
|
|
49
|
+
|
|
50
|
+
@stream.expect(:router, router)
|
|
51
|
+
@stream.expect(:storage, storage, [bogus.domain])
|
|
52
|
+
|
|
53
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
|
54
|
+
assert_nothing_raised { stanza.process }
|
|
55
|
+
assert @stream.verify
|
|
56
|
+
assert router.verify
|
|
57
|
+
assert storage.verify
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_message_to_offline_user_returns_error
|
|
61
|
+
hatter = Vines::User.new(:jid => 'hatter@wonderland.lit/cake')
|
|
62
|
+
node = node(%Q{<message to="#{hatter.jid}">hello!</message>})
|
|
63
|
+
|
|
64
|
+
router = MiniTest::Mock.new
|
|
65
|
+
router.expect(:local?, true, [node])
|
|
66
|
+
router.expect(:connected_resources, [], [hatter.jid])
|
|
67
|
+
|
|
68
|
+
storage = MiniTest::Mock.new
|
|
69
|
+
storage.expect(:find_user, hatter, [hatter.jid])
|
|
70
|
+
|
|
71
|
+
@stream.expect(:router, router)
|
|
72
|
+
@stream.expect(:storage, storage, [hatter.jid.domain])
|
|
73
|
+
|
|
74
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
|
75
|
+
assert_raise(Vines::StanzaErrors::ServiceUnavailable) { stanza.process }
|
|
76
|
+
assert @stream.verify
|
|
77
|
+
assert router.verify
|
|
78
|
+
assert storage.verify
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_message_to_local_user_in_different_domain_is_delivered
|
|
82
|
+
romeo = Vines::User.new(:jid => 'romeo@verona.lit/balcony')
|
|
83
|
+
node = node(%Q{<message to="#{romeo.jid}">hello!</message>})
|
|
84
|
+
|
|
85
|
+
recipient = MiniTest::Mock.new
|
|
86
|
+
router = MiniTest::Mock.new
|
|
87
|
+
router.expect(:local?, true, [node])
|
|
88
|
+
router.expect(:connected_resources, [recipient], [romeo.jid])
|
|
89
|
+
|
|
90
|
+
@stream.expect(:router, router)
|
|
91
|
+
@stream.expect(:broadcast, nil, [node, [recipient]])
|
|
92
|
+
|
|
93
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
|
94
|
+
assert_nothing_raised { stanza.process }
|
|
95
|
+
assert @stream.verify
|
|
96
|
+
assert router.verify
|
|
97
|
+
assert recipient.verify
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_message_to_remote_user_is_routed
|
|
101
|
+
alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
|
|
102
|
+
romeo = Vines::User.new(:jid => 'romeo@verona.lit/balcony')
|
|
103
|
+
node = node(%Q{<message to="#{romeo.jid}">hello!</message>})
|
|
104
|
+
expected = node(%Q{<message to="#{romeo.jid}" from="#{alice.jid}">hello!</message>})
|
|
105
|
+
|
|
106
|
+
router = MiniTest::Mock.new
|
|
107
|
+
router.expect(:local?, false, [node])
|
|
108
|
+
router.expect(:route, nil, [expected])
|
|
109
|
+
|
|
110
|
+
@stream.expect(:router, router)
|
|
111
|
+
@stream.expect(:user, alice)
|
|
112
|
+
|
|
113
|
+
stanza = Vines::Stanza::Message.new(node, @stream)
|
|
114
|
+
assert_nothing_raised { stanza.process }
|
|
115
|
+
assert @stream.verify
|
|
116
|
+
assert router.verify
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def node(xml)
|
|
122
|
+
Nokogiri::XML(xml).root
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'vines'
|
|
4
|
+
require 'ext/nokogiri'
|
|
5
|
+
require 'minitest/mock'
|
|
6
|
+
require 'test/unit'
|
|
7
|
+
|
|
8
|
+
class SubscribeTest < Test::Unit::TestCase
|
|
9
|
+
def test_outbound_subscribe_to_local_jid_but_missing_contact
|
|
10
|
+
alice = Vines::JID.new('alice@wonderland.lit/tea')
|
|
11
|
+
hatter = Vines::JID.new('hatter@wonderland.lit')
|
|
12
|
+
|
|
13
|
+
contact = Vines::Contact.new(:jid => hatter)
|
|
14
|
+
|
|
15
|
+
user = MiniTest::Mock.new
|
|
16
|
+
user.expect(:jid, alice)
|
|
17
|
+
user.expect(:request_subscription, nil, [hatter.to_s])
|
|
18
|
+
user.expect(:contact, contact, [hatter])
|
|
19
|
+
|
|
20
|
+
storage = MiniTest::Mock.new
|
|
21
|
+
storage.expect(:save_user, nil, [user])
|
|
22
|
+
storage.expect(:find_user, nil, [hatter])
|
|
23
|
+
|
|
24
|
+
recipient = MiniTest::Mock.new
|
|
25
|
+
recipient.expect(:user, Vines::User.new(:jid => hatter))
|
|
26
|
+
def recipient.nodes; @nodes; end
|
|
27
|
+
def recipient.write(node)
|
|
28
|
+
@nodes ||= []
|
|
29
|
+
@nodes << node
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
router = MiniTest::Mock.new
|
|
33
|
+
router.expect(:interested_resources, [recipient], [alice])
|
|
34
|
+
|
|
35
|
+
stream = MiniTest::Mock.new
|
|
36
|
+
stream.expect(:domain, 'wonderland.lit')
|
|
37
|
+
stream.expect(:storage, storage, ['wonderland.lit'])
|
|
38
|
+
stream.expect(:user, user)
|
|
39
|
+
stream.expect(:router, router)
|
|
40
|
+
stream.expect(:update_user_streams, nil, [user])
|
|
41
|
+
def stream.nodes; @nodes; end
|
|
42
|
+
def stream.write(node)
|
|
43
|
+
@nodes ||= []
|
|
44
|
+
@nodes << node
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
node = node(%q{<presence id="42" to="hatter@wonderland.lit" type="subscribe"/>})
|
|
48
|
+
stanza = Vines::Stanza::Presence::Subscribe.new(node, stream)
|
|
49
|
+
def stanza.route_iq; false; end
|
|
50
|
+
def stanza.inbound?; false; end
|
|
51
|
+
def stanza.local?; true; end
|
|
52
|
+
|
|
53
|
+
stanza.process
|
|
54
|
+
assert stream.verify
|
|
55
|
+
assert user.verify
|
|
56
|
+
assert storage.verify
|
|
57
|
+
assert router.verify
|
|
58
|
+
assert_equal 1, stream.nodes.size
|
|
59
|
+
assert_equal 1, recipient.nodes.size
|
|
60
|
+
|
|
61
|
+
expected = node(%q{<presence from="hatter@wonderland.lit" id="42" to="alice@wonderland.lit" type="unsubscribed"/>})
|
|
62
|
+
assert_equal expected, stream.nodes[0]
|
|
63
|
+
|
|
64
|
+
query = %q{<query xmlns="jabber:iq:roster"><item jid="hatter@wonderland.lit" subscription="none"/></query>}
|
|
65
|
+
expected = node(%Q{<iq to="alice@wonderland.lit/tea" type="set">#{query}</iq>})
|
|
66
|
+
recipient.nodes[0].remove_attribute('id') # id is random
|
|
67
|
+
assert_equal expected, recipient.nodes[0]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def node(xml)
|
|
73
|
+
Nokogiri::XML(xml).root
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'storage_tests'
|
|
4
|
+
require 'vines'
|
|
5
|
+
require 'test/unit'
|
|
6
|
+
|
|
7
|
+
class CouchDBTest < Test::Unit::TestCase
|
|
8
|
+
include StorageTests
|
|
9
|
+
|
|
10
|
+
URL = 'http://localhost:5984/xmpp_testcase'.freeze
|
|
11
|
+
|
|
12
|
+
def setup
|
|
13
|
+
EMLoop.new do
|
|
14
|
+
database(:put)
|
|
15
|
+
save_doc({'_id' => 'user:empty@wonderland.lit'})
|
|
16
|
+
|
|
17
|
+
save_doc({
|
|
18
|
+
'_id' => 'user:no_password@wonderland.lit',
|
|
19
|
+
'type' => 'User',
|
|
20
|
+
'foo' => 'bar'})
|
|
21
|
+
|
|
22
|
+
save_doc({
|
|
23
|
+
'_id' => 'user:clear_password@wonderland.lit',
|
|
24
|
+
'type' => 'User',
|
|
25
|
+
'password' => 'secret'})
|
|
26
|
+
|
|
27
|
+
save_doc({
|
|
28
|
+
'_id' => 'user:bcrypt_password@wonderland.lit',
|
|
29
|
+
'type' => 'User',
|
|
30
|
+
'password' => BCrypt::Password.create('secret')})
|
|
31
|
+
|
|
32
|
+
save_doc({
|
|
33
|
+
'_id' => 'user:full@wonderland.lit',
|
|
34
|
+
'type' => 'User',
|
|
35
|
+
'password' => BCrypt::Password.create('secret'),
|
|
36
|
+
'name' => 'Tester',
|
|
37
|
+
'roster' => {
|
|
38
|
+
'contact1@wonderland.lit' => {
|
|
39
|
+
'name' => 'Contact1',
|
|
40
|
+
'groups' => %w[Group1 Group2]
|
|
41
|
+
},
|
|
42
|
+
'contact2@wonderland.lit' => {
|
|
43
|
+
'name' => 'Contact2',
|
|
44
|
+
'groups' => %w[Group3 Group4]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
save_doc({
|
|
50
|
+
'_id' => 'vcard:full@wonderland.lit',
|
|
51
|
+
'type' => 'Vcard',
|
|
52
|
+
'card' => StorageTests::VCARD.to_xml
|
|
53
|
+
})
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def teardown
|
|
58
|
+
EMLoop.new do
|
|
59
|
+
database(:delete)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def save_doc(doc)
|
|
64
|
+
fiber = Fiber.current
|
|
65
|
+
http = EM::HttpRequest.new(URL).post(
|
|
66
|
+
:head => {'Content-Type' => 'application/json'},
|
|
67
|
+
:body => doc.to_json)
|
|
68
|
+
http.callback { fiber.resume }
|
|
69
|
+
http.errback { raise 'save_doc failed' }
|
|
70
|
+
Fiber.yield
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def database(method=:put)
|
|
74
|
+
fiber = Fiber.current
|
|
75
|
+
http = EM::HttpRequest.new(URL).send(method)
|
|
76
|
+
http.callback { fiber.resume }
|
|
77
|
+
http.errback { raise "#{method} database failed" }
|
|
78
|
+
Fiber.yield
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def storage
|
|
82
|
+
Vines::Storage::CouchDB.new do
|
|
83
|
+
host 'localhost'
|
|
84
|
+
port 5984
|
|
85
|
+
database 'xmpp_testcase'
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_init
|
|
90
|
+
EMLoop.new do
|
|
91
|
+
assert_raise(RuntimeError) { Vines::Storage::CouchDB.new {} }
|
|
92
|
+
assert_raise(RuntimeError) { Vines::Storage::CouchDB.new { host 'localhost' } }
|
|
93
|
+
assert_nothing_raised do
|
|
94
|
+
Vines::Storage::CouchDB.new do
|
|
95
|
+
host 'localhost'
|
|
96
|
+
port '5984'
|
|
97
|
+
database 'test'
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'vines'
|
|
4
|
+
require 'minitest/mock'
|
|
5
|
+
require 'test/unit'
|
|
6
|
+
|
|
7
|
+
class LdapTest < Test::Unit::TestCase
|
|
8
|
+
ALICE_DN = 'uid=alice@wondlerand.lit,ou=People,dc=wonderland,dc=lit'
|
|
9
|
+
CONTEXT = {}
|
|
10
|
+
|
|
11
|
+
def setup
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def teardown
|
|
15
|
+
CONTEXT.clear
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_missing_host_and_port
|
|
19
|
+
assert_raises(RuntimeError) do
|
|
20
|
+
Vines::Storage::Ldap.new(nil, nil) do
|
|
21
|
+
tls true
|
|
22
|
+
dn 'cn=Directory Manager'
|
|
23
|
+
password 'secr3t'
|
|
24
|
+
basedn 'dc=wonderland,dc=lit'
|
|
25
|
+
object_class 'person'
|
|
26
|
+
user_attr 'uid'
|
|
27
|
+
name_attr 'cn'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_default_host_and_port
|
|
33
|
+
assert_nothing_raised do
|
|
34
|
+
Vines::Storage::Ldap.new do
|
|
35
|
+
tls true
|
|
36
|
+
dn 'cn=Directory Manager'
|
|
37
|
+
password 'secr3t'
|
|
38
|
+
basedn 'dc=wonderland,dc=lit'
|
|
39
|
+
object_class 'person'
|
|
40
|
+
user_attr 'uid'
|
|
41
|
+
name_attr 'cn'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_configured_host_and_port
|
|
47
|
+
assert_nothing_raised do
|
|
48
|
+
Vines::Storage::Ldap.new('0.0.0.1', 42) do
|
|
49
|
+
tls true
|
|
50
|
+
dn 'cn=Directory Manager'
|
|
51
|
+
password 'secr3t'
|
|
52
|
+
basedn 'dc=wonderland,dc=lit'
|
|
53
|
+
object_class 'person'
|
|
54
|
+
user_attr 'uid'
|
|
55
|
+
name_attr 'cn'
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_missing_parameters
|
|
61
|
+
assert_raises(RuntimeError) do
|
|
62
|
+
Vines::Storage::Ldap.new {}
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_blank_parameters
|
|
67
|
+
assert_raises(RuntimeError) do
|
|
68
|
+
Vines::Storage::Ldap.new do
|
|
69
|
+
tls
|
|
70
|
+
dn
|
|
71
|
+
password
|
|
72
|
+
basedn
|
|
73
|
+
object_class
|
|
74
|
+
user_attr
|
|
75
|
+
name_attr
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Make sure we properly handle boolean false values.
|
|
81
|
+
def test_false_tls
|
|
82
|
+
assert_nothing_raised do
|
|
83
|
+
Vines::Storage::Ldap.new do
|
|
84
|
+
tls false
|
|
85
|
+
dn 'cn=Directory Manager'
|
|
86
|
+
password 'secr3t'
|
|
87
|
+
basedn 'dc=wonderland,dc=lit'
|
|
88
|
+
object_class 'person'
|
|
89
|
+
user_attr 'uid'
|
|
90
|
+
name_attr 'cn'
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_authenticate_with_missing_credentials
|
|
96
|
+
ldap = connect
|
|
97
|
+
assert_nil ldap.authenticate(nil, nil)
|
|
98
|
+
assert_nil ldap.authenticate('', '')
|
|
99
|
+
assert_nil ldap.authenticate('user', ' ')
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_authenticate_user_not_found
|
|
103
|
+
CONTEXT.merge!({
|
|
104
|
+
:mock_ldap => MiniTest::Mock.new,
|
|
105
|
+
:connect_calls => 0
|
|
106
|
+
})
|
|
107
|
+
ldap = connect
|
|
108
|
+
def ldap.connect(dn, password)
|
|
109
|
+
CONTEXT[:connect_calls] += 1
|
|
110
|
+
raise unless 'cn=Directory Manager' == dn
|
|
111
|
+
raise unless 'secr3t' == password
|
|
112
|
+
|
|
113
|
+
clas = Net::LDAP::Filter.eq('objectClass', 'person')
|
|
114
|
+
uid = Net::LDAP::Filter.eq('uid', 'alice@wonderland.lit')
|
|
115
|
+
|
|
116
|
+
mock = CONTEXT[:mock_ldap]
|
|
117
|
+
mock.expect(:search, [], [{:attributes => ['cn', 'mail'], :filter => clas & uid}])
|
|
118
|
+
end
|
|
119
|
+
assert_nil ldap.authenticate('alice@wonderland.lit', 'passw0rd')
|
|
120
|
+
assert CONTEXT[:mock_ldap]
|
|
121
|
+
assert_equal 1, CONTEXT[:connect_calls]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def test_authenticate_user_fails_bind
|
|
125
|
+
CONTEXT.merge!({
|
|
126
|
+
:credentials => [
|
|
127
|
+
{:dn => 'cn=Directory Manager', :password => 'secr3t'},
|
|
128
|
+
{:dn => ALICE_DN, :password => 'passw0rd'}
|
|
129
|
+
],
|
|
130
|
+
:mock_ldap => MiniTest::Mock.new,
|
|
131
|
+
:mock_entry => MiniTest::Mock.new,
|
|
132
|
+
:connect_calls => 0
|
|
133
|
+
})
|
|
134
|
+
ldap = connect
|
|
135
|
+
def ldap.connect(dn, password)
|
|
136
|
+
credentials = CONTEXT[:credentials][CONTEXT[:connect_calls]]
|
|
137
|
+
CONTEXT[:connect_calls] += 1
|
|
138
|
+
raise unless credentials[:dn] == dn && credentials[:password] == password
|
|
139
|
+
|
|
140
|
+
clas = Net::LDAP::Filter.eq('objectClass', 'person')
|
|
141
|
+
uid = Net::LDAP::Filter.eq('uid', 'alice@wonderland.lit')
|
|
142
|
+
|
|
143
|
+
entry = CONTEXT[:mock_entry]
|
|
144
|
+
entry.expect(:dn, ALICE_DN)
|
|
145
|
+
|
|
146
|
+
mock = CONTEXT[:mock_ldap]
|
|
147
|
+
mock.expect(:search, [entry], [{:attributes => ['cn', 'mail'], :filter => clas & uid}])
|
|
148
|
+
mock.expect(:bind, false)
|
|
149
|
+
end
|
|
150
|
+
assert_nil ldap.authenticate('alice@wonderland.lit', 'passw0rd')
|
|
151
|
+
assert_equal 2, CONTEXT[:connect_calls]
|
|
152
|
+
assert CONTEXT[:mock_entry].verify
|
|
153
|
+
assert CONTEXT[:mock_ldap].verify
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def test_authenticate_success
|
|
157
|
+
CONTEXT.merge!({
|
|
158
|
+
:credentials => [
|
|
159
|
+
{:dn => 'cn=Directory Manager', :password => 'secr3t'},
|
|
160
|
+
{:dn => ALICE_DN, :password => 'passw0rd'}
|
|
161
|
+
],
|
|
162
|
+
:mock_ldap => MiniTest::Mock.new,
|
|
163
|
+
:mock_entry => MiniTest::Mock.new,
|
|
164
|
+
:connect_calls => 0
|
|
165
|
+
})
|
|
166
|
+
ldap = connect
|
|
167
|
+
def ldap.connect(dn, password)
|
|
168
|
+
credentials = CONTEXT[:credentials][CONTEXT[:connect_calls]]
|
|
169
|
+
CONTEXT[:connect_calls] += 1
|
|
170
|
+
raise unless credentials[:dn] == dn && credentials[:password] == password
|
|
171
|
+
|
|
172
|
+
clas = Net::LDAP::Filter.eq('objectClass', 'person')
|
|
173
|
+
uid = Net::LDAP::Filter.eq('uid', 'alice@wonderland.lit')
|
|
174
|
+
|
|
175
|
+
entry = CONTEXT[:mock_entry]
|
|
176
|
+
entry.expect(:dn, ALICE_DN)
|
|
177
|
+
entry.expect(:[], ['Alice Liddell'], ['cn'])
|
|
178
|
+
|
|
179
|
+
mock = CONTEXT[:mock_ldap]
|
|
180
|
+
mock.expect(:search, [entry], [{:attributes => ['cn', 'mail'], :filter => clas & uid}])
|
|
181
|
+
mock.expect(:bind, true)
|
|
182
|
+
end
|
|
183
|
+
user = ldap.authenticate('alice@wonderland.lit', 'passw0rd')
|
|
184
|
+
assert_not_nil user
|
|
185
|
+
assert_equal 'alice@wonderland.lit', user.jid.to_s
|
|
186
|
+
assert_equal 'Alice Liddell', user.name
|
|
187
|
+
assert_equal [], user.roster
|
|
188
|
+
|
|
189
|
+
assert_equal 2, CONTEXT[:connect_calls]
|
|
190
|
+
assert CONTEXT[:mock_entry].verify
|
|
191
|
+
assert CONTEXT[:mock_ldap].verify
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
private
|
|
195
|
+
|
|
196
|
+
def connect
|
|
197
|
+
Vines::Storage::Ldap.new('0.0.0.0', 636) do
|
|
198
|
+
tls true
|
|
199
|
+
dn 'cn=Directory Manager'
|
|
200
|
+
password 'secr3t'
|
|
201
|
+
basedn 'dc=wonderland,dc=lit'
|
|
202
|
+
object_class 'person'
|
|
203
|
+
user_attr 'uid'
|
|
204
|
+
name_attr 'cn'
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|