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
@@ -0,0 +1,195 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::SASL do
|
6
|
+
subject { Vines::Stream::SASL.new(stream) }
|
7
|
+
let(:stream) { MiniTest::Mock.new }
|
8
|
+
let(:storage) { MiniTest::Mock.new }
|
9
|
+
let(:romeo) { Vines::User.new(jid: 'romeo@verona.lit') }
|
10
|
+
|
11
|
+
before do
|
12
|
+
def subject.log
|
13
|
+
Class.new do
|
14
|
+
def method_missing(*args)
|
15
|
+
# do nothing
|
16
|
+
end
|
17
|
+
end.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#plain_auth' do
|
22
|
+
before do
|
23
|
+
stream.expect :domain, 'verona.lit'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'fails with empty input' do
|
27
|
+
-> { subject.plain_auth(nil) }.must_raise Vines::SaslErrors::IncorrectEncoding
|
28
|
+
-> { subject.plain_auth('') }.must_raise Vines::SaslErrors::NotAuthorized
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'fails with plain text' do
|
32
|
+
-> { subject.plain_auth('bogus') }.must_raise Vines::SaslErrors::IncorrectEncoding
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'fails with incorrectly encoded base64 text' do
|
36
|
+
-> { subject.plain_auth('=dmVyb25hLmxpdA==') }.must_raise Vines::SaslErrors::IncorrectEncoding
|
37
|
+
-> { subject.plain_auth("dmVyb25hLmxpdA==\n") }.must_raise Vines::SaslErrors::IncorrectEncoding
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'fails when authzid does not match authcid username' do
|
41
|
+
encoded = Base64.strict_encode64("juliet@verona.lit\x00romeo\x00secr3t")
|
42
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::InvalidAuthzid
|
43
|
+
stream.verify
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'fails when authzid does not match authcid domain' do
|
47
|
+
encoded = Base64.strict_encode64("romeo@wonderland.lit\x00romeo\x00secr3t")
|
48
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::InvalidAuthzid
|
49
|
+
stream.verify
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'fails when username and password are missing' do
|
53
|
+
encoded = Base64.strict_encode64("\x00\x00")
|
54
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::NotAuthorized
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'fails when username is missing' do
|
58
|
+
encoded = Base64.strict_encode64("\x00\x00secr3t")
|
59
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::NotAuthorized
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'fails when password is missing with delimiter' do
|
63
|
+
encoded = Base64.strict_encode64("\x00romeo\x00")
|
64
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::NotAuthorized
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'fails when password is missing' do
|
68
|
+
encoded = Base64.strict_encode64("\x00romeo")
|
69
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::NotAuthorized
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'fails with invalid jid' do
|
73
|
+
encoded = Base64.strict_encode64("\x00#{'a' * 1024}\x00secr3t")
|
74
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::NotAuthorized
|
75
|
+
stream.verify
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'fails with invalid password' do
|
79
|
+
storage.expect :authenticate, nil, [romeo.jid, 'secr3t']
|
80
|
+
stream.expect :storage, storage
|
81
|
+
|
82
|
+
encoded = Base64.strict_encode64("\x00romeo\x00secr3t")
|
83
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::NotAuthorized
|
84
|
+
|
85
|
+
stream.verify
|
86
|
+
storage.verify
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'passes with valid password' do
|
90
|
+
storage.expect :authenticate, romeo, [romeo.jid, 'secr3t']
|
91
|
+
stream.expect :storage, storage
|
92
|
+
|
93
|
+
encoded = Base64.strict_encode64("\x00romeo\x00secr3t")
|
94
|
+
subject.plain_auth(encoded).must_equal romeo
|
95
|
+
|
96
|
+
stream.verify
|
97
|
+
storage.verify
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'passes with valid password and unicode jid' do
|
101
|
+
user = Vines::User.new(jid: 'piñata@verona.lit')
|
102
|
+
storage.expect :authenticate, user, [user.jid, 'secr3t']
|
103
|
+
stream.expect :storage, storage
|
104
|
+
|
105
|
+
encoded = Base64.strict_encode64("\x00piñata\x00secr3t")
|
106
|
+
subject.plain_auth(encoded).must_equal user
|
107
|
+
|
108
|
+
stream.verify
|
109
|
+
storage.verify
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'passes with valid password and authzid provided by strophe and blather' do
|
113
|
+
storage.expect :authenticate, romeo, [romeo.jid, 'secr3t']
|
114
|
+
stream.expect :storage, storage
|
115
|
+
|
116
|
+
encoded = Base64.strict_encode64("romeo@Verona.LIT\x00romeo\x00secr3t")
|
117
|
+
subject.plain_auth(encoded).must_equal romeo
|
118
|
+
|
119
|
+
stream.verify
|
120
|
+
storage.verify
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'passes with valid password and authzid provided by smack' do
|
124
|
+
storage.expect :authenticate, romeo, [romeo.jid, 'secr3t']
|
125
|
+
stream.expect :storage, storage
|
126
|
+
|
127
|
+
encoded = Base64.strict_encode64("romeo\x00romeo\x00secr3t")
|
128
|
+
subject.plain_auth(encoded).must_equal romeo
|
129
|
+
|
130
|
+
stream.verify
|
131
|
+
storage.verify
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'raises temporary-auth-failure when storage backend fails' do
|
135
|
+
storage = Class.new do
|
136
|
+
def authenticate(*args)
|
137
|
+
raise 'boom'
|
138
|
+
end
|
139
|
+
end.new
|
140
|
+
|
141
|
+
stream.expect :storage, storage
|
142
|
+
encoded = Base64.strict_encode64("\x00romeo\x00secr3t")
|
143
|
+
-> { subject.plain_auth(encoded) }.must_raise Vines::SaslErrors::TemporaryAuthFailure
|
144
|
+
stream.verify
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#external_auth' do
|
149
|
+
it 'fails with empty input' do
|
150
|
+
stream.expect :remote_domain, 'verona.lit'
|
151
|
+
-> { subject.external_auth(nil) }.must_raise Vines::SaslErrors::IncorrectEncoding
|
152
|
+
-> { subject.external_auth('') }.must_raise Vines::SaslErrors::InvalidAuthzid
|
153
|
+
stream.verify
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'fails with plain text' do
|
157
|
+
-> { subject.external_auth('bogus') }.must_raise Vines::SaslErrors::IncorrectEncoding
|
158
|
+
stream.verify
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'fails with incorrectly encoded base64 text' do
|
162
|
+
-> { subject.external_auth('=dmVyb25hLmxpdA==') }.must_raise Vines::SaslErrors::IncorrectEncoding
|
163
|
+
-> { subject.external_auth("dmVyb25hLmxpdA==\n") }.must_raise Vines::SaslErrors::IncorrectEncoding
|
164
|
+
stream.verify
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'passes with empty authzid and matching cert' do
|
168
|
+
stream.expect :remote_domain, 'verona.lit'
|
169
|
+
stream.expect :cert_domain_matches?, true, ['verona.lit']
|
170
|
+
subject.external_auth('=').must_equal true
|
171
|
+
stream.verify
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'fails with empty authzid and non-matching cert' do
|
175
|
+
stream.expect :remote_domain, 'verona.lit'
|
176
|
+
stream.expect :cert_domain_matches?, false, ['verona.lit']
|
177
|
+
-> { subject.external_auth('=') }.must_raise Vines::SaslErrors::NotAuthorized
|
178
|
+
stream.verify
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'fails when authzid does not match stream from address' do
|
182
|
+
stream.expect :remote_domain, 'not.verona.lit'
|
183
|
+
-> { subject.external_auth('dmVyb25hLmxpdA==') }.must_raise Vines::SaslErrors::InvalidAuthzid
|
184
|
+
stream.verify
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'passes when authzid matches stream from address' do
|
188
|
+
stream.expect :remote_domain, 'verona.lit'
|
189
|
+
stream.expect :remote_domain, 'verona.lit'
|
190
|
+
stream.expect :cert_domain_matches?, true, ['verona.lit']
|
191
|
+
subject.external_auth('dmVyb25hLmxpdA==').must_equal true
|
192
|
+
stream.verify
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::Server::Auth do
|
6
|
+
# disable logging for tests
|
7
|
+
Class.new.extend(Vines::Log).log.level = Logger::FATAL
|
8
|
+
|
9
|
+
subject { Vines::Stream::Server::Auth.new(stream) }
|
10
|
+
let(:stream) { MiniTest::Mock.new }
|
11
|
+
|
12
|
+
before do
|
13
|
+
class << stream
|
14
|
+
attr_accessor :remote_domain
|
15
|
+
end
|
16
|
+
stream.remote_domain = 'wonderland.lit'
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'when given a valid authzid' do
|
20
|
+
before do
|
21
|
+
stream.expect :cert_domain_matches?, true, ['wonderland.lit']
|
22
|
+
stream.expect :write, nil, ['<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>']
|
23
|
+
stream.expect :advance, nil, [Vines::Stream::Server::FinalRestart]
|
24
|
+
stream.expect :reset, nil
|
25
|
+
stream.expect :authentication_mechanisms, ['EXTERNAL']
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'passes external auth with empty authzid' do
|
29
|
+
node = external('=')
|
30
|
+
subject.node(node)
|
31
|
+
stream.verify
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'passes external auth with authzid matching from domain' do
|
35
|
+
node = external(Base64.strict_encode64('wonderland.lit'))
|
36
|
+
subject.node(node)
|
37
|
+
stream.verify
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'when given an invalid authzid' do
|
42
|
+
before do
|
43
|
+
stream.expect :write, nil, ['</stream:stream>']
|
44
|
+
stream.expect :close_connection_after_writing, nil
|
45
|
+
stream.expect :error, nil, [Vines::SaslErrors::InvalidAuthzid]
|
46
|
+
stream.expect :authentication_mechanisms, ['EXTERNAL']
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'fails external auth with mismatched from domain' do
|
50
|
+
node = external(Base64.strict_encode64('verona.lit'))
|
51
|
+
subject.node(node)
|
52
|
+
stream.verify
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def external(authzid)
|
59
|
+
node(%Q{<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="EXTERNAL">#{authzid}</auth>})
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::Server::Outbound::Auth do
|
6
|
+
before do
|
7
|
+
@stream = MiniTest::Mock.new
|
8
|
+
@state = Vines::Stream::Server::Outbound::Auth.new(@stream)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_invalid_element
|
12
|
+
node = node('<message/>')
|
13
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_invalid_sasl_element
|
17
|
+
node = node(%Q{<message xmlns="#{Vines::NAMESPACES[:sasl]}"/>})
|
18
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_missing_namespace
|
22
|
+
node = node('<stream:features/>')
|
23
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_invalid_namespace
|
27
|
+
node = node('<stream:features xmlns="bogus"/>')
|
28
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_missing_mechanisms
|
32
|
+
node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams"/>})
|
33
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_missing_mechanisms_namespace
|
37
|
+
node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams"><mechanisms/></stream:features>})
|
38
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_missing_mechanism
|
42
|
+
mechanisms = %q{<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>}
|
43
|
+
node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams">#{mechanisms}</stream:features>})
|
44
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_missing_mechanism_text
|
48
|
+
mechanisms = %q{<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism></mechanism></mechanisms>}
|
49
|
+
node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams">#{mechanisms}</stream:features>})
|
50
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_invalid_mechanism_text
|
54
|
+
mechanisms = %q{<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>BOGUS</mechanism></mechanisms>}
|
55
|
+
node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams">#{mechanisms}</stream:features>})
|
56
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_valid_mechanism
|
60
|
+
@stream.expect(:domain, 'wonderland.lit')
|
61
|
+
expected = %Q{<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="EXTERNAL">d29uZGVybGFuZC5saXQ=</auth>}
|
62
|
+
@stream.expect(:write, nil, [expected])
|
63
|
+
@stream.expect(:advance, nil, [Vines::Stream::Server::Outbound::AuthResult.new(@stream)])
|
64
|
+
mechanisms = %q{<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>EXTERNAL</mechanism></mechanisms>}
|
65
|
+
node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams">#{mechanisms}</stream:features>})
|
66
|
+
@state.node(node)
|
67
|
+
assert @stream.verify
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def node(xml)
|
73
|
+
Nokogiri::XML(xml).root
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::Server::Ready do
|
6
|
+
subject { Vines::Stream::Server::Ready.new(stream, nil) }
|
7
|
+
let(:stream) { MiniTest::Mock.new }
|
8
|
+
|
9
|
+
SERVER_STANZAS = []
|
10
|
+
|
11
|
+
before do
|
12
|
+
def subject.to_stanza(node)
|
13
|
+
Vines::Stanza.from_node(node, stream).tap do |stanza|
|
14
|
+
def stanza.process
|
15
|
+
SERVER_STANZAS << self
|
16
|
+
end if stanza
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
SERVER_STANZAS.clear
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'processes a valid node' do
|
26
|
+
config = MiniTest::Mock.new
|
27
|
+
config.expect(:local_jid?, true, [Vines::JID.new('romeo@verona.lit')])
|
28
|
+
|
29
|
+
stream.expect(:config, config)
|
30
|
+
stream.expect(:remote_domain, 'wonderland.lit')
|
31
|
+
stream.expect(:domain, 'verona.lit')
|
32
|
+
stream.expect(:user=, nil, [Vines::User.new(jid: 'alice@wonderland.lit')])
|
33
|
+
|
34
|
+
node = node(%Q{<message from="alice@wonderland.lit" to="romeo@verona.lit"/>})
|
35
|
+
subject.node(node)
|
36
|
+
assert_equal 1, SERVER_STANZAS.size
|
37
|
+
assert stream.verify
|
38
|
+
assert config.verify
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'raises unsupported-stanza-type stream error' do
|
42
|
+
node = node('<bogus/>')
|
43
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::UnsupportedStanzaType
|
44
|
+
assert SERVER_STANZAS.empty?
|
45
|
+
assert stream.verify
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'raises improper-addressing stream error when to address is missing' do
|
49
|
+
node = node(%Q{<message from="alice@wonderland.lit"/>})
|
50
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::ImproperAddressing
|
51
|
+
assert SERVER_STANZAS.empty?
|
52
|
+
assert stream.verify
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises jid-malformed stanza error when to address is invalid' do
|
56
|
+
node = node(%Q{<message from="alice@wonderland.lit" to=" "/>})
|
57
|
+
-> { subject.node(node) }.must_raise Vines::StanzaErrors::JidMalformed
|
58
|
+
assert SERVER_STANZAS.empty?
|
59
|
+
assert stream.verify
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'raises improper-addressing stream error' do
|
63
|
+
node = node(%Q{<message to="romeo@verona.lit"/>})
|
64
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::ImproperAddressing
|
65
|
+
assert SERVER_STANZAS.empty?
|
66
|
+
assert stream.verify
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'raises jid-malformed stanza error for invalid from address' do
|
70
|
+
node = node(%Q{<message from=" " to="romeo@verona.lit"/>})
|
71
|
+
-> { subject.node(node) }.must_raise Vines::StanzaErrors::JidMalformed
|
72
|
+
assert SERVER_STANZAS.empty?
|
73
|
+
assert stream.verify
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'raises invalid-from stream error' do
|
77
|
+
stream.expect(:remote_domain, 'wonderland.lit')
|
78
|
+
node = node(%Q{<message from="alice@bogus.lit" to="romeo@verona.lit"/>})
|
79
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::InvalidFrom
|
80
|
+
assert SERVER_STANZAS.empty?
|
81
|
+
assert stream.verify
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'raises host-unknown stream error' do
|
85
|
+
stream.expect(:remote_domain, 'wonderland.lit')
|
86
|
+
stream.expect(:domain, 'verona.lit')
|
87
|
+
node = node(%Q{<message from="alice@wonderland.lit" to="romeo@bogus.lit"/>})
|
88
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::HostUnknown
|
89
|
+
assert SERVER_STANZAS.empty?
|
90
|
+
assert stream.verify
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def node(xml)
|
96
|
+
Nokogiri::XML(xml).root
|
97
|
+
end
|
98
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'vines'
|
5
|
+
require 'ext/nokogiri'
|
6
|
+
require 'minitest/autorun'
|
7
|
+
|
8
|
+
class MiniTest::Spec
|
9
|
+
|
10
|
+
# Build an <iq> xml node with the given attributes. This is useful as a
|
11
|
+
# quick way to build a node to use as expected stanza output from a
|
12
|
+
# Stream#write call.
|
13
|
+
#
|
14
|
+
# options - The Hash of xml attributes to include on the iq element. Attribute
|
15
|
+
# values of nil or empty? are excluded from the generated element.
|
16
|
+
# :body - The String xml content to include in the iq element.
|
17
|
+
#
|
18
|
+
# Examples
|
19
|
+
#
|
20
|
+
# iq(from: from, id: 42, to: to, type: 'result', body: card)
|
21
|
+
#
|
22
|
+
# Returns a Nokogiri::XML::Node.
|
23
|
+
def iq(options)
|
24
|
+
body = options.delete(:body)
|
25
|
+
options.delete_if {|k, v| v.nil? || v.to_s.empty? }
|
26
|
+
attrs = options.map {|k, v| "#{k}=\"#{v}\"" }.join(' ')
|
27
|
+
node("<iq #{attrs}>#{body}</iq>")
|
28
|
+
end
|
29
|
+
|
30
|
+
# Parse xml into a nokogiri node. Strip excessive whitespace from the xml
|
31
|
+
# content before parsing because it affects comparisons in MiniTest::Mock
|
32
|
+
# expectations.
|
33
|
+
#
|
34
|
+
# xml - The String of xml content to parse.
|
35
|
+
#
|
36
|
+
# Returns a Nokogiri::XML::Node.
|
37
|
+
def node(xml)
|
38
|
+
xml = xml.strip.gsub(/\n|\s{2,}/, '')
|
39
|
+
Nokogiri::XML(xml).root
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::TokenBucket do
|
6
|
+
subject { Vines::TokenBucket.new(10, 1) }
|
7
|
+
|
8
|
+
it 'raises with invalid capacity and rate values' do
|
9
|
+
-> { Vines::TokenBucket.new(0, 1) }.must_raise ArgumentError
|
10
|
+
-> { Vines::TokenBucket.new(1, 0) }.must_raise ArgumentError
|
11
|
+
-> { Vines::TokenBucket.new(-1, 1) }.must_raise ArgumentError
|
12
|
+
-> { Vines::TokenBucket.new(1, -1) }.must_raise ArgumentError
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'does not allow taking a negative number of tokens' do
|
16
|
+
-> { subject.take(-1) }.must_raise ArgumentError
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'does not allow taking more tokens than its capacity' do
|
20
|
+
refute subject.take(11)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'allows taking all tokens, but no more' do
|
24
|
+
assert subject.take(10)
|
25
|
+
refute subject.take(1)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'refills over time' do
|
29
|
+
assert subject.take(10)
|
30
|
+
refute subject.take(1)
|
31
|
+
Time.stub(:new, Time.now + 1) do
|
32
|
+
assert subject.take(1)
|
33
|
+
refute subject.take(1)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'does not refill over capacity' do
|
38
|
+
assert subject.take(10)
|
39
|
+
refute subject.take(1)
|
40
|
+
Time.stub(:new, Time.now + 15) do
|
41
|
+
refute subject.take(11)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/test/user_test.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::User do
|
6
|
+
subject { Vines::User.new(jid: 'alice@wonderland.lit', name: 'Alice', password: 'secr3t') }
|
7
|
+
|
8
|
+
describe 'user equality checks' do
|
9
|
+
let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit') }
|
10
|
+
let(:hatter) { Vines::User.new(jid: 'hatter@wonderland.lit') }
|
11
|
+
|
12
|
+
it 'uses class in equality check' do
|
13
|
+
(subject <=> 42).must_be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'is equal to itself' do
|
17
|
+
assert subject == subject
|
18
|
+
assert subject.eql?(subject)
|
19
|
+
assert subject.hash == subject.hash
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'is equal to another user with the same jid' do
|
23
|
+
assert subject == alice
|
24
|
+
assert subject.eql?(alice)
|
25
|
+
assert subject.hash == alice.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'is not equal to a different jid' do
|
29
|
+
refute subject == hatter
|
30
|
+
refute subject.eql?(hatter)
|
31
|
+
refute subject.hash == hatter.hash
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'initialize' do
|
36
|
+
it 'raises when not given a jid' do
|
37
|
+
-> { Vines::User.new }.must_raise ArgumentError
|
38
|
+
-> { Vines::User.new(jid: '') }.must_raise ArgumentError
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'has an empty roster' do
|
42
|
+
subject.roster.wont_be_nil
|
43
|
+
subject.roster.size.must_equal 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#update_from' do
|
48
|
+
let(:updated) { Vines::User.new(jid: 'alice2@wonderland.lit', name: 'Alice 2', password: "secr3t 2") }
|
49
|
+
|
50
|
+
before do
|
51
|
+
subject.roster << Vines::Contact.new(jid: 'hatter@wonderland.lit', name: "Hatter")
|
52
|
+
updated.roster << Vines::Contact.new(jid: 'cat@wonderland.lit', name: "Cheshire")
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'updates jid, name, and password' do
|
56
|
+
subject.update_from(updated)
|
57
|
+
subject.jid.to_s.must_equal 'alice@wonderland.lit'
|
58
|
+
subject.name.must_equal 'Alice 2'
|
59
|
+
subject.password.must_equal 'secr3t 2'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'overwrites the entire roster' do
|
63
|
+
subject.update_from(updated)
|
64
|
+
subject.roster.size.must_equal 1
|
65
|
+
subject.roster.first.must_equal updated.roster.first
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'clones roster entries' do
|
69
|
+
subject.update_from(updated)
|
70
|
+
updated.roster.first.name = 'Updated Contact 2'
|
71
|
+
subject.roster.first.name.must_equal 'Cheshire'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#to_roster_xml' do
|
76
|
+
let(:expected) do
|
77
|
+
node(%q{
|
78
|
+
<iq id="42" type="result">
|
79
|
+
<query xmlns="jabber:iq:roster">
|
80
|
+
<item jid="a@wonderland.lit" name="Contact 1" subscription="none"><group>A</group><group>B</group></item>
|
81
|
+
<item jid="b@wonderland.lit" name="Contact 2" subscription="none"><group>C</group></item>
|
82
|
+
</query>
|
83
|
+
</iq>
|
84
|
+
})
|
85
|
+
end
|
86
|
+
|
87
|
+
before do
|
88
|
+
subject.roster << Vines::Contact.new(jid: 'b@wonderland.lit', name: "Contact 2", groups: %w[C])
|
89
|
+
subject.roster << Vines::Contact.new(jid: 'a@wonderland.lit', name: "Contact 1", groups: %w[B A])
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'sorts group names' do
|
93
|
+
subject.to_roster_xml(42).must_equal expected
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/vines.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require './lib/vines/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'diaspora-vines'
|
5
|
+
s.version = Vines::VERSION
|
6
|
+
s.summary = %q[Diaspora-vines is a Vines fork build for diaspora integration.]
|
7
|
+
s.description = %q[Diaspora-vines is a Vines fork build for diaspora integration. DO NOT use it unless you know what you are doing!]
|
8
|
+
|
9
|
+
s.authors = ['David Graham','Lukas Matt']
|
10
|
+
s.email = ['david@negativecode.com','lukas@zauberstuhl.de']
|
11
|
+
s.homepage = 'https://diasporafoundation.org'
|
12
|
+
s.license = 'MIT'
|
13
|
+
|
14
|
+
s.files = Dir['[A-Z]*', 'vines.gemspec', '{bin,lib,conf,web}/**/*'] - ['Gemfile.lock']
|
15
|
+
s.test_files = Dir['test/**/*']
|
16
|
+
s.executables = %w[vines]
|
17
|
+
s.require_path = 'lib'
|
18
|
+
|
19
|
+
s.add_dependency 'bcrypt', '~> 3.1'
|
20
|
+
s.add_dependency 'em-hiredis', '~> 0.1.1'
|
21
|
+
s.add_dependency 'eventmachine', '~> 1.0.3'
|
22
|
+
s.add_dependency 'http_parser.rb', '~> 0.5.3'
|
23
|
+
s.add_dependency 'net-ldap', '~> 0.3.1'
|
24
|
+
s.add_dependency 'nokogiri', '>= 1.5.10'
|
25
|
+
|
26
|
+
s.add_development_dependency 'minitest', '~> 5.3'
|
27
|
+
s.add_development_dependency 'rake', '~> 10.3'
|
28
|
+
|
29
|
+
s.required_ruby_version = '>= 1.9.3'
|
30
|
+
end
|