lygneo-vines 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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/daemon.rb +78 -0
  30. data/lib/vines/error.rb +150 -0
  31. data/lib/vines/follower.rb +111 -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/error_test.rb +58 -0
  125. data/test/ext/nokogiri.rb +14 -0
  126. data/test/follower_test.rb +102 -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::Follower.new(jid: 'hatter@wonderland.lit', name: "Hatter")
52
+ updated.roster << Vines::Follower.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 Follower 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="Follower 1" subscription="none"><group>A</group><group>B</group></item>
81
+ <item jid="b@wonderland.lit" name="Follower 2" subscription="none"><group>C</group></item>
82
+ </query>
83
+ </iq>
84
+ })
85
+ end
86
+
87
+ before do
88
+ subject.roster << Vines::Follower.new(jid: 'b@wonderland.lit', name: "Follower 2", groups: %w[C])
89
+ subject.roster << Vines::Follower.new(jid: 'a@wonderland.lit', name: "Follower 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 = 'lygneo-vines'
5
+ s.version = Vines::VERSION
6
+ s.summary = %q[Lygneo-vines is a Vines fork build for lygneo integration.]
7
+ s.description = %q[Lygneo-vines is a Vines fork build for lygneo 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://lygneofoundation.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