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.
Files changed (174) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +19 -0
  4. data/README.md +7 -0
  5. data/Rakefile +23 -0
  6. data/bin/vines +4 -0
  7. data/conf/certs/README +39 -0
  8. data/conf/certs/ca-bundle.crt +3895 -0
  9. data/conf/config.rb +42 -0
  10. data/lib/vines/cli.rb +132 -0
  11. data/lib/vines/cluster/connection.rb +26 -0
  12. data/lib/vines/cluster/publisher.rb +55 -0
  13. data/lib/vines/cluster/pubsub.rb +92 -0
  14. data/lib/vines/cluster/sessions.rb +125 -0
  15. data/lib/vines/cluster/subscriber.rb +108 -0
  16. data/lib/vines/cluster.rb +246 -0
  17. data/lib/vines/command/bcrypt.rb +12 -0
  18. data/lib/vines/command/cert.rb +50 -0
  19. data/lib/vines/command/init.rb +68 -0
  20. data/lib/vines/command/ldap.rb +38 -0
  21. data/lib/vines/command/restart.rb +12 -0
  22. data/lib/vines/command/schema.rb +24 -0
  23. data/lib/vines/command/start.rb +28 -0
  24. data/lib/vines/command/stop.rb +18 -0
  25. data/lib/vines/config/host.rb +125 -0
  26. data/lib/vines/config/port.rb +132 -0
  27. data/lib/vines/config/pubsub.rb +108 -0
  28. data/lib/vines/config.rb +223 -0
  29. data/lib/vines/contact.rb +111 -0
  30. data/lib/vines/daemon.rb +78 -0
  31. data/lib/vines/error.rb +150 -0
  32. data/lib/vines/jid.rb +95 -0
  33. data/lib/vines/kit.rb +23 -0
  34. data/lib/vines/log.rb +24 -0
  35. data/lib/vines/router.rb +179 -0
  36. data/lib/vines/stanza/iq/auth.rb +18 -0
  37. data/lib/vines/stanza/iq/disco_info.rb +45 -0
  38. data/lib/vines/stanza/iq/disco_items.rb +29 -0
  39. data/lib/vines/stanza/iq/error.rb +16 -0
  40. data/lib/vines/stanza/iq/ping.rb +16 -0
  41. data/lib/vines/stanza/iq/private_storage.rb +83 -0
  42. data/lib/vines/stanza/iq/query.rb +10 -0
  43. data/lib/vines/stanza/iq/result.rb +16 -0
  44. data/lib/vines/stanza/iq/roster.rb +140 -0
  45. data/lib/vines/stanza/iq/session.rb +17 -0
  46. data/lib/vines/stanza/iq/vcard.rb +56 -0
  47. data/lib/vines/stanza/iq/version.rb +25 -0
  48. data/lib/vines/stanza/iq.rb +48 -0
  49. data/lib/vines/stanza/message.rb +40 -0
  50. data/lib/vines/stanza/presence/error.rb +23 -0
  51. data/lib/vines/stanza/presence/probe.rb +37 -0
  52. data/lib/vines/stanza/presence/subscribe.rb +42 -0
  53. data/lib/vines/stanza/presence/subscribed.rb +51 -0
  54. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  55. data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
  56. data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
  57. data/lib/vines/stanza/presence.rb +141 -0
  58. data/lib/vines/stanza/pubsub/create.rb +39 -0
  59. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  60. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  61. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  62. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  63. data/lib/vines/stanza/pubsub.rb +22 -0
  64. data/lib/vines/stanza.rb +175 -0
  65. data/lib/vines/storage/ldap.rb +71 -0
  66. data/lib/vines/storage/local.rb +139 -0
  67. data/lib/vines/storage/null.rb +39 -0
  68. data/lib/vines/storage/sql.rb +138 -0
  69. data/lib/vines/storage.rb +239 -0
  70. data/lib/vines/store.rb +110 -0
  71. data/lib/vines/stream/client/auth.rb +74 -0
  72. data/lib/vines/stream/client/auth_restart.rb +29 -0
  73. data/lib/vines/stream/client/bind.rb +72 -0
  74. data/lib/vines/stream/client/bind_restart.rb +24 -0
  75. data/lib/vines/stream/client/closed.rb +13 -0
  76. data/lib/vines/stream/client/ready.rb +17 -0
  77. data/lib/vines/stream/client/session.rb +210 -0
  78. data/lib/vines/stream/client/start.rb +27 -0
  79. data/lib/vines/stream/client/tls.rb +38 -0
  80. data/lib/vines/stream/client.rb +84 -0
  81. data/lib/vines/stream/component/handshake.rb +26 -0
  82. data/lib/vines/stream/component/ready.rb +23 -0
  83. data/lib/vines/stream/component/start.rb +19 -0
  84. data/lib/vines/stream/component.rb +58 -0
  85. data/lib/vines/stream/http/auth.rb +22 -0
  86. data/lib/vines/stream/http/bind.rb +32 -0
  87. data/lib/vines/stream/http/bind_restart.rb +37 -0
  88. data/lib/vines/stream/http/ready.rb +29 -0
  89. data/lib/vines/stream/http/request.rb +172 -0
  90. data/lib/vines/stream/http/session.rb +120 -0
  91. data/lib/vines/stream/http/sessions.rb +65 -0
  92. data/lib/vines/stream/http/start.rb +23 -0
  93. data/lib/vines/stream/http.rb +157 -0
  94. data/lib/vines/stream/parser.rb +79 -0
  95. data/lib/vines/stream/sasl.rb +128 -0
  96. data/lib/vines/stream/server/auth.rb +13 -0
  97. data/lib/vines/stream/server/auth_restart.rb +13 -0
  98. data/lib/vines/stream/server/final_restart.rb +21 -0
  99. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  100. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  101. data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
  102. data/lib/vines/stream/server/outbound/final_features.rb +28 -0
  103. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  104. data/lib/vines/stream/server/outbound/start.rb +20 -0
  105. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  106. data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
  107. data/lib/vines/stream/server/ready.rb +24 -0
  108. data/lib/vines/stream/server/start.rb +13 -0
  109. data/lib/vines/stream/server/tls.rb +13 -0
  110. data/lib/vines/stream/server.rb +150 -0
  111. data/lib/vines/stream/state.rb +60 -0
  112. data/lib/vines/stream.rb +247 -0
  113. data/lib/vines/token_bucket.rb +55 -0
  114. data/lib/vines/user.rb +123 -0
  115. data/lib/vines/version.rb +6 -0
  116. data/lib/vines/xmpp_server.rb +25 -0
  117. data/lib/vines.rb +203 -0
  118. data/test/cluster/publisher_test.rb +57 -0
  119. data/test/cluster/sessions_test.rb +47 -0
  120. data/test/cluster/subscriber_test.rb +109 -0
  121. data/test/config/host_test.rb +369 -0
  122. data/test/config/pubsub_test.rb +187 -0
  123. data/test/config_test.rb +732 -0
  124. data/test/contact_test.rb +102 -0
  125. data/test/error_test.rb +58 -0
  126. data/test/ext/nokogiri.rb +14 -0
  127. data/test/jid_test.rb +147 -0
  128. data/test/kit_test.rb +31 -0
  129. data/test/router_test.rb +243 -0
  130. data/test/stanza/iq/disco_info_test.rb +78 -0
  131. data/test/stanza/iq/disco_items_test.rb +49 -0
  132. data/test/stanza/iq/private_storage_test.rb +184 -0
  133. data/test/stanza/iq/roster_test.rb +229 -0
  134. data/test/stanza/iq/session_test.rb +25 -0
  135. data/test/stanza/iq/vcard_test.rb +146 -0
  136. data/test/stanza/iq/version_test.rb +64 -0
  137. data/test/stanza/iq_test.rb +70 -0
  138. data/test/stanza/message_test.rb +126 -0
  139. data/test/stanza/presence/probe_test.rb +50 -0
  140. data/test/stanza/presence/subscribe_test.rb +83 -0
  141. data/test/stanza/pubsub/create_test.rb +116 -0
  142. data/test/stanza/pubsub/delete_test.rb +169 -0
  143. data/test/stanza/pubsub/publish_test.rb +309 -0
  144. data/test/stanza/pubsub/subscribe_test.rb +205 -0
  145. data/test/stanza/pubsub/unsubscribe_test.rb +148 -0
  146. data/test/stanza_test.rb +85 -0
  147. data/test/storage/ldap_test.rb +201 -0
  148. data/test/storage/local_test.rb +59 -0
  149. data/test/storage/mock_redis.rb +97 -0
  150. data/test/storage/null_test.rb +29 -0
  151. data/test/storage/storage_tests.rb +182 -0
  152. data/test/storage_test.rb +85 -0
  153. data/test/store_test.rb +130 -0
  154. data/test/stream/client/auth_test.rb +137 -0
  155. data/test/stream/client/ready_test.rb +47 -0
  156. data/test/stream/client/session_test.rb +27 -0
  157. data/test/stream/component/handshake_test.rb +52 -0
  158. data/test/stream/component/ready_test.rb +103 -0
  159. data/test/stream/component/start_test.rb +39 -0
  160. data/test/stream/http/auth_test.rb +70 -0
  161. data/test/stream/http/ready_test.rb +86 -0
  162. data/test/stream/http/request_test.rb +209 -0
  163. data/test/stream/http/sessions_test.rb +49 -0
  164. data/test/stream/http/start_test.rb +50 -0
  165. data/test/stream/parser_test.rb +122 -0
  166. data/test/stream/sasl_test.rb +195 -0
  167. data/test/stream/server/auth_test.rb +61 -0
  168. data/test/stream/server/outbound/auth_test.rb +75 -0
  169. data/test/stream/server/ready_test.rb +98 -0
  170. data/test/test_helper.rb +42 -0
  171. data/test/token_bucket_test.rb +44 -0
  172. data/test/user_test.rb +96 -0
  173. data/vines.gemspec +30 -0
  174. 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
@@ -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