diaspora-vines 0.1.2
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +7 -0
- data/Rakefile +23 -0
- data/bin/vines +4 -0
- data/conf/certs/README +39 -0
- data/conf/certs/ca-bundle.crt +3895 -0
- data/conf/config.rb +42 -0
- data/lib/vines/cli.rb +132 -0
- data/lib/vines/cluster/connection.rb +26 -0
- data/lib/vines/cluster/publisher.rb +55 -0
- data/lib/vines/cluster/pubsub.rb +92 -0
- data/lib/vines/cluster/sessions.rb +125 -0
- data/lib/vines/cluster/subscriber.rb +108 -0
- data/lib/vines/cluster.rb +246 -0
- data/lib/vines/command/bcrypt.rb +12 -0
- data/lib/vines/command/cert.rb +50 -0
- data/lib/vines/command/init.rb +68 -0
- data/lib/vines/command/ldap.rb +38 -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/host.rb +125 -0
- data/lib/vines/config/port.rb +132 -0
- data/lib/vines/config/pubsub.rb +108 -0
- data/lib/vines/config.rb +223 -0
- data/lib/vines/contact.rb +111 -0
- data/lib/vines/daemon.rb +78 -0
- data/lib/vines/error.rb +150 -0
- data/lib/vines/jid.rb +95 -0
- data/lib/vines/kit.rb +23 -0
- data/lib/vines/log.rb +24 -0
- data/lib/vines/router.rb +179 -0
- data/lib/vines/stanza/iq/auth.rb +18 -0
- data/lib/vines/stanza/iq/disco_info.rb +45 -0
- data/lib/vines/stanza/iq/disco_items.rb +29 -0
- data/lib/vines/stanza/iq/error.rb +16 -0
- data/lib/vines/stanza/iq/ping.rb +16 -0
- data/lib/vines/stanza/iq/private_storage.rb +83 -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 +140 -0
- data/lib/vines/stanza/iq/session.rb +17 -0
- data/lib/vines/stanza/iq/vcard.rb +56 -0
- data/lib/vines/stanza/iq/version.rb +25 -0
- data/lib/vines/stanza/iq.rb +48 -0
- data/lib/vines/stanza/message.rb +40 -0
- data/lib/vines/stanza/presence/error.rb +23 -0
- data/lib/vines/stanza/presence/probe.rb +37 -0
- data/lib/vines/stanza/presence/subscribe.rb +42 -0
- data/lib/vines/stanza/presence/subscribed.rb +51 -0
- data/lib/vines/stanza/presence/unavailable.rb +15 -0
- data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
- data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
- data/lib/vines/stanza/presence.rb +141 -0
- data/lib/vines/stanza/pubsub/create.rb +39 -0
- data/lib/vines/stanza/pubsub/delete.rb +41 -0
- data/lib/vines/stanza/pubsub/publish.rb +66 -0
- data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
- data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
- data/lib/vines/stanza/pubsub.rb +22 -0
- data/lib/vines/stanza.rb +175 -0
- data/lib/vines/storage/ldap.rb +71 -0
- data/lib/vines/storage/local.rb +139 -0
- data/lib/vines/storage/null.rb +39 -0
- data/lib/vines/storage/sql.rb +138 -0
- data/lib/vines/storage.rb +239 -0
- data/lib/vines/store.rb +110 -0
- data/lib/vines/stream/client/auth.rb +74 -0
- data/lib/vines/stream/client/auth_restart.rb +29 -0
- data/lib/vines/stream/client/bind.rb +72 -0
- data/lib/vines/stream/client/bind_restart.rb +24 -0
- data/lib/vines/stream/client/closed.rb +13 -0
- data/lib/vines/stream/client/ready.rb +17 -0
- data/lib/vines/stream/client/session.rb +210 -0
- data/lib/vines/stream/client/start.rb +27 -0
- data/lib/vines/stream/client/tls.rb +38 -0
- data/lib/vines/stream/client.rb +84 -0
- data/lib/vines/stream/component/handshake.rb +26 -0
- data/lib/vines/stream/component/ready.rb +23 -0
- data/lib/vines/stream/component/start.rb +19 -0
- data/lib/vines/stream/component.rb +58 -0
- data/lib/vines/stream/http/auth.rb +22 -0
- data/lib/vines/stream/http/bind.rb +32 -0
- data/lib/vines/stream/http/bind_restart.rb +37 -0
- data/lib/vines/stream/http/ready.rb +29 -0
- data/lib/vines/stream/http/request.rb +172 -0
- data/lib/vines/stream/http/session.rb +120 -0
- data/lib/vines/stream/http/sessions.rb +65 -0
- data/lib/vines/stream/http/start.rb +23 -0
- data/lib/vines/stream/http.rb +157 -0
- data/lib/vines/stream/parser.rb +79 -0
- data/lib/vines/stream/sasl.rb +128 -0
- data/lib/vines/stream/server/auth.rb +13 -0
- data/lib/vines/stream/server/auth_restart.rb +13 -0
- data/lib/vines/stream/server/final_restart.rb +21 -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 +32 -0
- data/lib/vines/stream/server/outbound/final_features.rb +28 -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 +34 -0
- data/lib/vines/stream/server/ready.rb +24 -0
- data/lib/vines/stream/server/start.rb +13 -0
- data/lib/vines/stream/server/tls.rb +13 -0
- data/lib/vines/stream/server.rb +150 -0
- data/lib/vines/stream/state.rb +60 -0
- data/lib/vines/stream.rb +247 -0
- data/lib/vines/token_bucket.rb +55 -0
- data/lib/vines/user.rb +123 -0
- data/lib/vines/version.rb +6 -0
- data/lib/vines/xmpp_server.rb +25 -0
- data/lib/vines.rb +203 -0
- data/test/cluster/publisher_test.rb +57 -0
- data/test/cluster/sessions_test.rb +47 -0
- data/test/cluster/subscriber_test.rb +109 -0
- data/test/config/host_test.rb +369 -0
- data/test/config/pubsub_test.rb +187 -0
- data/test/config_test.rb +732 -0
- data/test/contact_test.rb +102 -0
- data/test/error_test.rb +58 -0
- data/test/ext/nokogiri.rb +14 -0
- data/test/jid_test.rb +147 -0
- data/test/kit_test.rb +31 -0
- data/test/router_test.rb +243 -0
- data/test/stanza/iq/disco_info_test.rb +78 -0
- data/test/stanza/iq/disco_items_test.rb +49 -0
- data/test/stanza/iq/private_storage_test.rb +184 -0
- data/test/stanza/iq/roster_test.rb +229 -0
- data/test/stanza/iq/session_test.rb +25 -0
- data/test/stanza/iq/vcard_test.rb +146 -0
- data/test/stanza/iq/version_test.rb +64 -0
- data/test/stanza/iq_test.rb +70 -0
- data/test/stanza/message_test.rb +126 -0
- data/test/stanza/presence/probe_test.rb +50 -0
- data/test/stanza/presence/subscribe_test.rb +83 -0
- data/test/stanza/pubsub/create_test.rb +116 -0
- data/test/stanza/pubsub/delete_test.rb +169 -0
- data/test/stanza/pubsub/publish_test.rb +309 -0
- data/test/stanza/pubsub/subscribe_test.rb +205 -0
- data/test/stanza/pubsub/unsubscribe_test.rb +148 -0
- data/test/stanza_test.rb +85 -0
- data/test/storage/ldap_test.rb +201 -0
- data/test/storage/local_test.rb +59 -0
- data/test/storage/mock_redis.rb +97 -0
- data/test/storage/null_test.rb +29 -0
- data/test/storage/storage_tests.rb +182 -0
- data/test/storage_test.rb +85 -0
- data/test/store_test.rb +130 -0
- data/test/stream/client/auth_test.rb +137 -0
- data/test/stream/client/ready_test.rb +47 -0
- data/test/stream/client/session_test.rb +27 -0
- data/test/stream/component/handshake_test.rb +52 -0
- data/test/stream/component/ready_test.rb +103 -0
- data/test/stream/component/start_test.rb +39 -0
- data/test/stream/http/auth_test.rb +70 -0
- data/test/stream/http/ready_test.rb +86 -0
- data/test/stream/http/request_test.rb +209 -0
- data/test/stream/http/sessions_test.rb +49 -0
- data/test/stream/http/start_test.rb +50 -0
- data/test/stream/parser_test.rb +122 -0
- data/test/stream/sasl_test.rb +195 -0
- data/test/stream/server/auth_test.rb +61 -0
- data/test/stream/server/outbound/auth_test.rb +75 -0
- data/test/stream/server/ready_test.rb +98 -0
- data/test/test_helper.rb +42 -0
- data/test/token_bucket_test.rb +44 -0
- data/test/user_test.rb +96 -0
- data/vines.gemspec +30 -0
- metadata +387 -0
data/test/stanza_test.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stanza do
|
6
|
+
subject { Vines::Stanza::Message.new(xml, stream) }
|
7
|
+
let(:alice) { Vines::JID.new('alice@wonderland.lit/tea') }
|
8
|
+
let(:romeo) { Vines::JID.new('romeo@verona.lit/balcony') }
|
9
|
+
let(:stream) { MiniTest::Mock.new }
|
10
|
+
let(:config) do
|
11
|
+
Vines::Config.new do
|
12
|
+
host 'wonderland.lit' do
|
13
|
+
storage(:fs) { dir Dir.tmpdir }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'when stanza contains no addresses' do
|
19
|
+
let(:xml) { node(%Q{<message>hello!</message>}) }
|
20
|
+
|
21
|
+
it 'validates them as nil' do
|
22
|
+
subject.validate_to.must_be_nil
|
23
|
+
subject.validate_from.must_be_nil
|
24
|
+
stream.verify
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'when stanza contains valid addresses' do
|
29
|
+
let(:xml) { node(%Q{<message from="#{alice}" to="#{romeo}">hello!</message>}) }
|
30
|
+
|
31
|
+
it 'validates and returns JID objects' do
|
32
|
+
subject.validate_to.must_equal romeo
|
33
|
+
subject.validate_from.must_equal alice
|
34
|
+
stream.verify
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'when stanza contains invalid addresses' do
|
39
|
+
let(:xml) { node(%Q{<message from="a lice@wonderland.lit" to="romeo@v erona.lit">hello!</message>}) }
|
40
|
+
|
41
|
+
it 'raises a jid-malformed stanza error' do
|
42
|
+
-> { subject.validate_to }.must_raise Vines::StanzaErrors::JidMalformed
|
43
|
+
-> { subject.validate_from }.must_raise Vines::StanzaErrors::JidMalformed
|
44
|
+
stream.verify
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'when receiving a non-routable stanza type' do
|
49
|
+
let(:xml) { node('<auth/>') }
|
50
|
+
|
51
|
+
it 'handles locally rather than routing' do
|
52
|
+
subject.local?.must_equal true
|
53
|
+
stream.verify
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'when stanza is missing a to address' do
|
58
|
+
let(:xml) { node(%Q{<message>hello!</message>}) }
|
59
|
+
|
60
|
+
it 'handles locally rather than routing' do
|
61
|
+
subject.local?.must_equal true
|
62
|
+
stream.verify
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'when stanza is addressed to a local jid' do
|
67
|
+
let(:xml) { node(%Q{<message to="#{alice}">hello!</message>}) }
|
68
|
+
|
69
|
+
it 'handles locally rather than routing' do
|
70
|
+
stream.expect :config, config
|
71
|
+
subject.local?.must_equal true
|
72
|
+
stream.verify
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'when stanza is addressed to a remote jid' do
|
77
|
+
let(:xml) { node(%Q{<message to="#{romeo}">hello!</message>}) }
|
78
|
+
|
79
|
+
it 'is not considered a local stanza' do
|
80
|
+
stream.expect :config, config
|
81
|
+
subject.local?.must_equal false
|
82
|
+
stream.verify
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Storage::Ldap do
|
6
|
+
ALICE_DN = 'uid=alice@wondlerand.lit,ou=People,dc=wonderland,dc=lit'
|
7
|
+
CONTEXT = {}
|
8
|
+
|
9
|
+
after do
|
10
|
+
CONTEXT.clear
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#initialize' do
|
14
|
+
it 'raises with missing host and port' do
|
15
|
+
assert_raises(RuntimeError) do
|
16
|
+
Vines::Storage::Ldap.new(nil, nil) do
|
17
|
+
tls true
|
18
|
+
dn 'cn=Directory Manager'
|
19
|
+
password 'secr3t'
|
20
|
+
basedn 'dc=wonderland,dc=lit'
|
21
|
+
object_class 'person'
|
22
|
+
user_attr 'uid'
|
23
|
+
name_attr 'cn'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'does not raise when using default host and port' do
|
29
|
+
Vines::Storage::Ldap.new do
|
30
|
+
tls true
|
31
|
+
dn 'cn=Directory Manager'
|
32
|
+
password 'secr3t'
|
33
|
+
basedn 'dc=wonderland,dc=lit'
|
34
|
+
object_class 'person'
|
35
|
+
user_attr 'uid'
|
36
|
+
name_attr 'cn'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'does not raise when given a host and port' do
|
41
|
+
Vines::Storage::Ldap.new('0.0.0.1', 42) do
|
42
|
+
tls true
|
43
|
+
dn 'cn=Directory Manager'
|
44
|
+
password 'secr3t'
|
45
|
+
basedn 'dc=wonderland,dc=lit'
|
46
|
+
object_class 'person'
|
47
|
+
user_attr 'uid'
|
48
|
+
name_attr 'cn'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'raises when given no parameters' do
|
53
|
+
assert_raises(RuntimeError) do
|
54
|
+
Vines::Storage::Ldap.new {}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'raises when given blank parameters' do
|
59
|
+
assert_raises(RuntimeError) do
|
60
|
+
Vines::Storage::Ldap.new do
|
61
|
+
tls
|
62
|
+
dn
|
63
|
+
password
|
64
|
+
basedn
|
65
|
+
object_class
|
66
|
+
user_attr
|
67
|
+
name_attr
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'handles boolean false values without raising' do
|
73
|
+
Vines::Storage::Ldap.new do
|
74
|
+
tls false
|
75
|
+
dn 'cn=Directory Manager'
|
76
|
+
password 'secr3t'
|
77
|
+
basedn 'dc=wonderland,dc=lit'
|
78
|
+
object_class 'person'
|
79
|
+
user_attr 'uid'
|
80
|
+
name_attr 'cn'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#authenticate' do
|
86
|
+
it 'fails with invalid credentials' do
|
87
|
+
ldap = connect
|
88
|
+
assert_nil ldap.authenticate(nil, nil)
|
89
|
+
assert_nil ldap.authenticate('', '')
|
90
|
+
assert_nil ldap.authenticate('alice@wonderland.lit', ' ')
|
91
|
+
assert_nil ldap.authenticate('bogus"jid@wonderland.lit', 'password')
|
92
|
+
assert_nil ldap.authenticate(Vines::JID.new('alice@wonderland.lit'), '')
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'fails when user not found' do
|
96
|
+
CONTEXT.merge!({
|
97
|
+
:mock_ldap => MiniTest::Mock.new,
|
98
|
+
:connect_calls => 0
|
99
|
+
})
|
100
|
+
ldap = connect
|
101
|
+
def ldap.connect(dn, password)
|
102
|
+
CONTEXT[:connect_calls] += 1
|
103
|
+
raise unless 'cn=Directory Manager' == dn
|
104
|
+
raise unless 'secr3t' == password
|
105
|
+
|
106
|
+
clas = Net::LDAP::Filter.eq('objectClass', 'person')
|
107
|
+
uid = Net::LDAP::Filter.eq('uid', 'alice@wonderland.lit')
|
108
|
+
|
109
|
+
mock = CONTEXT[:mock_ldap]
|
110
|
+
mock.expect(:search, [], [{:attributes => ['cn', 'mail'], :filter => clas & uid}])
|
111
|
+
end
|
112
|
+
assert_nil ldap.authenticate('alice@wonderland.lit', 'passw0rd')
|
113
|
+
assert CONTEXT[:mock_ldap]
|
114
|
+
assert_equal 1, CONTEXT[:connect_calls]
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'fails when user cannot bind' do
|
118
|
+
CONTEXT.merge!({
|
119
|
+
:credentials => [
|
120
|
+
{:dn => 'cn=Directory Manager', :password => 'secr3t'},
|
121
|
+
{:dn => ALICE_DN, :password => 'passw0rd'}
|
122
|
+
],
|
123
|
+
:mock_ldap => MiniTest::Mock.new,
|
124
|
+
:mock_entry => MiniTest::Mock.new,
|
125
|
+
:connect_calls => 0
|
126
|
+
})
|
127
|
+
ldap = connect
|
128
|
+
def ldap.connect(dn, password)
|
129
|
+
credentials = CONTEXT[:credentials][CONTEXT[:connect_calls]]
|
130
|
+
CONTEXT[:connect_calls] += 1
|
131
|
+
raise unless credentials[:dn] == dn && credentials[:password] == password
|
132
|
+
|
133
|
+
clas = Net::LDAP::Filter.eq('objectClass', 'person')
|
134
|
+
uid = Net::LDAP::Filter.eq('uid', 'alice@wonderland.lit')
|
135
|
+
|
136
|
+
entry = CONTEXT[:mock_entry]
|
137
|
+
entry.expect(:dn, ALICE_DN)
|
138
|
+
|
139
|
+
mock = CONTEXT[:mock_ldap]
|
140
|
+
mock.expect(:search, [entry], [{:attributes => ['cn', 'mail'], :filter => clas & uid}])
|
141
|
+
mock.expect(:bind, false)
|
142
|
+
end
|
143
|
+
assert_nil ldap.authenticate('alice@wonderland.lit', 'passw0rd')
|
144
|
+
assert_equal 2, CONTEXT[:connect_calls]
|
145
|
+
assert CONTEXT[:mock_entry].verify
|
146
|
+
assert CONTEXT[:mock_ldap].verify
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'authenticates successfully with proper credentials' do
|
150
|
+
CONTEXT.merge!({
|
151
|
+
:credentials => [
|
152
|
+
{:dn => 'cn=Directory Manager', :password => 'secr3t'},
|
153
|
+
{:dn => ALICE_DN, :password => 'passw0rd'}
|
154
|
+
],
|
155
|
+
:mock_ldap => MiniTest::Mock.new,
|
156
|
+
:mock_entry => MiniTest::Mock.new,
|
157
|
+
:connect_calls => 0
|
158
|
+
})
|
159
|
+
ldap = connect
|
160
|
+
def ldap.connect(dn, password)
|
161
|
+
credentials = CONTEXT[:credentials][CONTEXT[:connect_calls]]
|
162
|
+
CONTEXT[:connect_calls] += 1
|
163
|
+
raise unless credentials[:dn] == dn && credentials[:password] == password
|
164
|
+
|
165
|
+
clas = Net::LDAP::Filter.eq('objectClass', 'person')
|
166
|
+
uid = Net::LDAP::Filter.eq('uid', 'alice@wonderland.lit')
|
167
|
+
|
168
|
+
entry = CONTEXT[:mock_entry]
|
169
|
+
entry.expect(:dn, ALICE_DN)
|
170
|
+
entry.expect(:[], ['Alice Liddell'], ['cn'])
|
171
|
+
|
172
|
+
mock = CONTEXT[:mock_ldap]
|
173
|
+
mock.expect(:search, [entry], [{:attributes => ['cn', 'mail'], :filter => clas & uid}])
|
174
|
+
mock.expect(:bind, true)
|
175
|
+
end
|
176
|
+
user = ldap.authenticate('alice@wonderland.lit', 'passw0rd')
|
177
|
+
refute_nil user
|
178
|
+
assert_equal 'alice@wonderland.lit', user.jid.to_s
|
179
|
+
assert_equal 'Alice Liddell', user.name
|
180
|
+
assert_equal [], user.roster
|
181
|
+
|
182
|
+
assert_equal 2, CONTEXT[:connect_calls]
|
183
|
+
assert CONTEXT[:mock_entry].verify
|
184
|
+
assert CONTEXT[:mock_ldap].verify
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
def connect
|
191
|
+
Vines::Storage::Ldap.new('0.0.0.0', 636) do
|
192
|
+
tls true
|
193
|
+
dn 'cn=Directory Manager'
|
194
|
+
password 'secr3t'
|
195
|
+
basedn 'dc=wonderland,dc=lit'
|
196
|
+
object_class 'person'
|
197
|
+
user_attr 'uid'
|
198
|
+
name_attr 'cn'
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'storage_tests'
|
4
|
+
require 'test_helper'
|
5
|
+
|
6
|
+
describe Vines::Storage::Local do
|
7
|
+
include StorageTests
|
8
|
+
|
9
|
+
DIR = Dir.mktmpdir
|
10
|
+
|
11
|
+
def setup
|
12
|
+
Dir.mkdir(DIR) unless File.exists?(DIR)
|
13
|
+
%w[user vcard fragment].each do |d|
|
14
|
+
Dir.mkdir(File.join(DIR, d))
|
15
|
+
end
|
16
|
+
|
17
|
+
files = {
|
18
|
+
:empty => "#{DIR}/user/empty@wonderland.lit",
|
19
|
+
:no_pass => "#{DIR}/user/no_password@wonderland.lit",
|
20
|
+
:clear_pass => "#{DIR}/user/clear_password@wonderland.lit",
|
21
|
+
:bcrypt => "#{DIR}/user/bcrypt_password@wonderland.lit",
|
22
|
+
:full => "#{DIR}/user/full@wonderland.lit",
|
23
|
+
:vcard => "#{DIR}/vcard/full@wonderland.lit",
|
24
|
+
:fragment => "#{DIR}/fragment/full@wonderland.lit-#{StorageTests::FRAGMENT_ID}"
|
25
|
+
}
|
26
|
+
File.open(files[:empty], 'w') {|f| f.write('') }
|
27
|
+
File.open(files[:no_pass], 'w') {|f| f.write('foo: bar') }
|
28
|
+
File.open(files[:clear_pass], 'w') {|f| f.write('password: secret') }
|
29
|
+
File.open(files[:bcrypt], 'w') {|f| f.write("password: #{BCrypt::Password.create('secret')}") }
|
30
|
+
File.open(files[:full], 'w') do |f|
|
31
|
+
f.puts("password: #{BCrypt::Password.create('secret')}")
|
32
|
+
f.puts("name: Tester")
|
33
|
+
f.puts("roster:")
|
34
|
+
f.puts(" contact1@wonderland.lit:")
|
35
|
+
f.puts(" name: Contact1")
|
36
|
+
f.puts(" groups: [Group1, Group2]")
|
37
|
+
f.puts(" contact2@wonderland.lit:")
|
38
|
+
f.puts(" name: Contact2")
|
39
|
+
f.puts(" groups: [Group3, Group4]")
|
40
|
+
end
|
41
|
+
File.open(files[:vcard], 'w') {|f| f.write(StorageTests::VCARD.to_xml) }
|
42
|
+
File.open(files[:fragment], 'w') {|f| f.write(StorageTests::FRAGMENT.to_xml) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def teardown
|
46
|
+
FileUtils.remove_entry_secure(DIR)
|
47
|
+
end
|
48
|
+
|
49
|
+
def storage
|
50
|
+
Vines::Storage::Local.new { dir DIR }
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_init
|
54
|
+
assert_raises(RuntimeError) { Vines::Storage::Local.new {} }
|
55
|
+
assert_raises(RuntimeError) { Vines::Storage::Local.new { dir 'bogus' } }
|
56
|
+
assert_raises(RuntimeError) { Vines::Storage::Local.new { dir '/sbin' } }
|
57
|
+
Vines::Storage::Local.new { dir DIR } # shouldn't raise an error
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# A mock redis storage implementation that saves data to an in-memory Hash.
|
4
|
+
class MockRedis
|
5
|
+
attr_reader :db
|
6
|
+
|
7
|
+
# Mimic em-hiredis behavior.
|
8
|
+
def self.defer(method)
|
9
|
+
old = instance_method(method)
|
10
|
+
define_method method do |*args, &block|
|
11
|
+
result = old.bind(self).call(*args)
|
12
|
+
deferred = EM::DefaultDeferrable.new
|
13
|
+
deferred.callback(&block) if block
|
14
|
+
EM.next_tick { deferred.succeed(result) }
|
15
|
+
deferred
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@db = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def del(key)
|
24
|
+
@db.delete(key)
|
25
|
+
end
|
26
|
+
defer :del
|
27
|
+
|
28
|
+
def get(key)
|
29
|
+
@db[key]
|
30
|
+
end
|
31
|
+
defer :get
|
32
|
+
|
33
|
+
def set(key, value)
|
34
|
+
@db[key] = value
|
35
|
+
end
|
36
|
+
defer :set
|
37
|
+
|
38
|
+
def hget(key, field)
|
39
|
+
@db[key][field] rescue nil
|
40
|
+
end
|
41
|
+
defer :hget
|
42
|
+
|
43
|
+
def hdel(key, field)
|
44
|
+
@db[key].delete(field) rescue nil
|
45
|
+
end
|
46
|
+
defer :hdel
|
47
|
+
|
48
|
+
def hgetall(key)
|
49
|
+
(@db[key] || {}).map do |k, v|
|
50
|
+
[k, v]
|
51
|
+
end.flatten
|
52
|
+
end
|
53
|
+
defer :hgetall
|
54
|
+
|
55
|
+
def hset(key, field, value)
|
56
|
+
@db[key] ||= {}
|
57
|
+
@db[key][field] = value
|
58
|
+
end
|
59
|
+
defer :hset
|
60
|
+
|
61
|
+
def hmset(key, *args)
|
62
|
+
@db[key] = Hash[*args]
|
63
|
+
end
|
64
|
+
defer :hmset
|
65
|
+
|
66
|
+
def sadd(key, obj)
|
67
|
+
@db[key] ||= Set.new
|
68
|
+
@db[key] << obj
|
69
|
+
end
|
70
|
+
defer :sadd
|
71
|
+
|
72
|
+
def srem(key, obj)
|
73
|
+
@db[key].delete(obj) rescue nil
|
74
|
+
end
|
75
|
+
defer :srem
|
76
|
+
|
77
|
+
def smembers
|
78
|
+
@db[key].to_a rescue []
|
79
|
+
end
|
80
|
+
defer :smembers
|
81
|
+
|
82
|
+
def flushdb
|
83
|
+
@db.clear
|
84
|
+
end
|
85
|
+
defer :flushdb
|
86
|
+
|
87
|
+
def multi
|
88
|
+
@transaction = true
|
89
|
+
end
|
90
|
+
defer :multi
|
91
|
+
|
92
|
+
def exec
|
93
|
+
raise 'transaction must start with multi' unless @transaction
|
94
|
+
@transaction = false
|
95
|
+
end
|
96
|
+
defer :exec
|
97
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Storage::Null do
|
6
|
+
before do
|
7
|
+
@storage = Vines::Storage::Null.new
|
8
|
+
@user = Vines::User.new(jid: 'alice@wonderland.lit')
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_find_user_returns_nil
|
12
|
+
assert_nil @storage.find_user(@user.jid)
|
13
|
+
@storage.save_user(@user)
|
14
|
+
assert_nil @storage.find_user(@user.jid)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_find_vcard_returns_nil
|
18
|
+
assert_nil @storage.find_vcard(@user.jid)
|
19
|
+
@storage.save_vcard(@user.jid, 'card')
|
20
|
+
assert_nil @storage.find_vcard(@user.jid)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_find_fragment_returns_nil
|
24
|
+
assert_nil @storage.find_fragment(@user.jid, 'node')
|
25
|
+
@storage.save_fragment(@user.jid, 'node')
|
26
|
+
assert_nil @storage.find_fragment(@user.jid, 'node')
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
# Mixin methods for storage implementation test classes. The behavioral
|
6
|
+
# tests are the same regardless of implementation so share those methods
|
7
|
+
# here.
|
8
|
+
module StorageTests
|
9
|
+
FRAGMENT_ID = Digest::SHA1.hexdigest("characters:urn:wonderland")
|
10
|
+
|
11
|
+
FRAGMENT = Nokogiri::XML(%q{
|
12
|
+
<characters xmlns="urn:wonderland">
|
13
|
+
<character>Alice</character>
|
14
|
+
</characters>
|
15
|
+
}.strip).root
|
16
|
+
|
17
|
+
VCARD = Nokogiri::XML(%q{
|
18
|
+
<vCard xmlns="vcard-temp">
|
19
|
+
<FN>Alice in Wonderland</FN>
|
20
|
+
</vCard>
|
21
|
+
}.strip).root
|
22
|
+
|
23
|
+
class EMLoop
|
24
|
+
def initialize
|
25
|
+
EM.run do
|
26
|
+
Fiber.new do
|
27
|
+
yield
|
28
|
+
EM.stop
|
29
|
+
end.resume
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_authenticate
|
35
|
+
EMLoop.new do
|
36
|
+
db = storage
|
37
|
+
assert_nil db.authenticate(nil, nil)
|
38
|
+
assert_nil db.authenticate(nil, 'secret')
|
39
|
+
assert_nil db.authenticate('bogus', nil)
|
40
|
+
assert_nil db.authenticate('bogus', 'secret')
|
41
|
+
assert_nil db.authenticate('empty@wonderland.lit', 'secret')
|
42
|
+
assert_nil db.authenticate('no_password@wonderland.lit', 'secret')
|
43
|
+
assert_nil db.authenticate('clear_password@wonderland.lit', 'secret')
|
44
|
+
|
45
|
+
user = db.authenticate('bcrypt_password@wonderland.lit', 'secret')
|
46
|
+
refute_nil user
|
47
|
+
assert_equal('bcrypt_password@wonderland.lit', user.jid.to_s)
|
48
|
+
|
49
|
+
user = db.authenticate('full@wonderland.lit', 'secret')
|
50
|
+
refute_nil user
|
51
|
+
assert_equal 'Tester', user.name
|
52
|
+
assert_equal 'full@wonderland.lit', user.jid.to_s
|
53
|
+
|
54
|
+
assert_equal 2, user.roster.length
|
55
|
+
assert_equal 'contact1@wonderland.lit', user.roster[0].jid.to_s
|
56
|
+
assert_equal 'Contact1', user.roster[0].name
|
57
|
+
assert_equal 2, user.roster[0].groups.length
|
58
|
+
assert_equal 'Group1', user.roster[0].groups[0]
|
59
|
+
assert_equal 'Group2', user.roster[0].groups[1]
|
60
|
+
|
61
|
+
assert_equal 'contact2@wonderland.lit', user.roster[1].jid.to_s
|
62
|
+
assert_equal 'Contact2', user.roster[1].name
|
63
|
+
assert_equal 2, user.roster[1].groups.length
|
64
|
+
assert_equal 'Group3', user.roster[1].groups[0]
|
65
|
+
assert_equal 'Group4', user.roster[1].groups[1]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_find_user
|
70
|
+
EMLoop.new do
|
71
|
+
db = storage
|
72
|
+
user = db.find_user(nil)
|
73
|
+
assert_nil user
|
74
|
+
|
75
|
+
user = db.find_user('full@wonderland.lit')
|
76
|
+
refute_nil user
|
77
|
+
assert_equal 'full@wonderland.lit', user.jid.to_s
|
78
|
+
|
79
|
+
user = db.find_user(Vines::JID.new('full@wonderland.lit'))
|
80
|
+
refute_nil user
|
81
|
+
assert_equal 'full@wonderland.lit', user.jid.to_s
|
82
|
+
|
83
|
+
user = db.find_user(Vines::JID.new('full@wonderland.lit/resource'))
|
84
|
+
refute_nil user
|
85
|
+
assert_equal 'full@wonderland.lit', user.jid.to_s
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_save_user
|
90
|
+
EMLoop.new do
|
91
|
+
db = storage
|
92
|
+
user = Vines::User.new(
|
93
|
+
:jid => 'save_user@domain.tld/resource1',
|
94
|
+
:name => 'Save User',
|
95
|
+
:password => 'secret')
|
96
|
+
user.roster << Vines::Contact.new(
|
97
|
+
:jid => 'contact1@domain.tld/resource2',
|
98
|
+
:name => 'Contact 1')
|
99
|
+
db.save_user(user)
|
100
|
+
user = db.find_user('save_user@domain.tld')
|
101
|
+
refute_nil user
|
102
|
+
assert_equal 'save_user@domain.tld', user.jid.to_s
|
103
|
+
assert_equal 'Save User', user.name
|
104
|
+
assert_equal 1, user.roster.length
|
105
|
+
assert_equal 'contact1@domain.tld', user.roster[0].jid.to_s
|
106
|
+
assert_equal 'Contact 1', user.roster[0].name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_find_vcard
|
111
|
+
EMLoop.new do
|
112
|
+
db = storage
|
113
|
+
card = db.find_vcard(nil)
|
114
|
+
assert_nil card
|
115
|
+
|
116
|
+
card = db.find_vcard('full@wonderland.lit')
|
117
|
+
refute_nil card
|
118
|
+
assert_equal VCARD, card
|
119
|
+
|
120
|
+
card = db.find_vcard(Vines::JID.new('full@wonderland.lit'))
|
121
|
+
refute_nil card
|
122
|
+
assert_equal VCARD, card
|
123
|
+
|
124
|
+
card = db.find_vcard(Vines::JID.new('full@wonderland.lit/resource'))
|
125
|
+
refute_nil card
|
126
|
+
assert_equal VCARD, card
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_save_vcard
|
131
|
+
EMLoop.new do
|
132
|
+
db = storage
|
133
|
+
db.save_user(Vines::User.new(:jid => 'save_user@domain.tld'))
|
134
|
+
db.save_vcard('save_user@domain.tld/resource1', VCARD)
|
135
|
+
card = db.find_vcard('save_user@domain.tld')
|
136
|
+
refute_nil card
|
137
|
+
assert_equal VCARD, card
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_find_fragment
|
142
|
+
EMLoop.new do
|
143
|
+
db = storage
|
144
|
+
root = Nokogiri::XML(%q{<characters xmlns="urn:wonderland"/>}).root
|
145
|
+
bad_name = Nokogiri::XML(%q{<not_characters xmlns="urn:wonderland"/>}).root
|
146
|
+
bad_ns = Nokogiri::XML(%q{<characters xmlns="not:wonderland"/>}).root
|
147
|
+
|
148
|
+
node = db.find_fragment(nil, nil)
|
149
|
+
assert_nil node
|
150
|
+
|
151
|
+
node = db.find_fragment('full@wonderland.lit', bad_name)
|
152
|
+
assert_nil node
|
153
|
+
|
154
|
+
node = db.find_fragment('full@wonderland.lit', bad_ns)
|
155
|
+
assert_nil node
|
156
|
+
|
157
|
+
node = db.find_fragment('full@wonderland.lit', root)
|
158
|
+
refute_nil node
|
159
|
+
assert_equal FRAGMENT, node
|
160
|
+
|
161
|
+
node = db.find_fragment(Vines::JID.new('full@wonderland.lit'), root)
|
162
|
+
refute_nil node
|
163
|
+
assert_equal FRAGMENT, node
|
164
|
+
|
165
|
+
node = db.find_fragment(Vines::JID.new('full@wonderland.lit/resource'), root)
|
166
|
+
refute_nil node
|
167
|
+
assert_equal FRAGMENT, node
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_save_fragment
|
172
|
+
EMLoop.new do
|
173
|
+
db = storage
|
174
|
+
root = Nokogiri::XML(%q{<characters xmlns="urn:wonderland"/>}).root
|
175
|
+
db.save_user(Vines::User.new(:jid => 'save_user@domain.tld'))
|
176
|
+
db.save_fragment('save_user@domain.tld/resource1', FRAGMENT)
|
177
|
+
node = db.find_fragment('save_user@domain.tld', root)
|
178
|
+
refute_nil node
|
179
|
+
assert_equal FRAGMENT, node
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|