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,85 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'storage_tests'
4
+ require 'test_helper'
5
+
6
+ describe Vines::Storage do
7
+ ALICE = 'alice@wonderland.lit'.freeze
8
+
9
+ class MockLdapStorage < Vines::Storage
10
+ attr_reader :authenticate_calls, :find_user_calls, :save_user_calls
11
+
12
+ def initialize(found_user=nil)
13
+ @found_user = found_user
14
+ @authenticate_calls = @find_user_calls = @save_user_calls = 0
15
+ @ldap = Class.new do
16
+ attr_accessor :user, :auth
17
+ def authenticate(username, password)
18
+ @auth ||= []
19
+ @auth << [username, password]
20
+ @user
21
+ end
22
+ end.new
23
+ end
24
+
25
+ def authenticate(username, password)
26
+ @authenticate_calls += 1
27
+ nil
28
+ end
29
+ wrap_ldap :authenticate
30
+
31
+ def find_user(jid)
32
+ @find_user_calls += 1
33
+ @found_user
34
+ end
35
+
36
+ def save_user(user)
37
+ @save_user_calls += 1
38
+ end
39
+ end
40
+
41
+ describe '#authenticate_with_ldap' do
42
+ it 'fails when given a bad password' do
43
+ StorageTests::EMLoop.new do
44
+ storage = MockLdapStorage.new
45
+ storage.ldap.user = nil
46
+ user = storage.authenticate(ALICE, 'bogus')
47
+ assert_nil user
48
+ assert_equal 0, storage.authenticate_calls
49
+ assert_equal 0, storage.find_user_calls
50
+ assert_equal 0, storage.save_user_calls
51
+ assert_equal [ALICE, 'bogus'], storage.ldap.auth.first
52
+ end
53
+ end
54
+
55
+ it 'succeeds when user exists in database' do
56
+ StorageTests::EMLoop.new do
57
+ alice = Vines::User.new(:jid => ALICE)
58
+ storage = MockLdapStorage.new(alice)
59
+ storage.ldap.user = alice
60
+ user = storage.authenticate(ALICE, 'secr3t')
61
+ refute_nil user
62
+ assert_equal ALICE, user.jid.to_s
63
+ assert_equal 0, storage.authenticate_calls
64
+ assert_equal 1, storage.find_user_calls
65
+ assert_equal 0, storage.save_user_calls
66
+ assert_equal [ALICE, 'secr3t'], storage.ldap.auth.first
67
+ end
68
+ end
69
+
70
+ it 'succeeds and saves user to the database' do
71
+ StorageTests::EMLoop.new do
72
+ alice = Vines::User.new(:jid => ALICE)
73
+ storage = MockLdapStorage.new
74
+ storage.ldap.user = alice
75
+ user = storage.authenticate(ALICE, 'secr3t')
76
+ refute_nil user
77
+ assert_equal ALICE, user.jid.to_s
78
+ assert_equal 0, storage.authenticate_calls
79
+ assert_equal 1, storage.find_user_calls
80
+ assert_equal 1, storage.save_user_calls
81
+ assert_equal [ALICE, 'secr3t'], storage.ldap.auth.first
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,130 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Store do
6
+ before do
7
+ dir = 'conf/certs'
8
+
9
+ domain, key = certificate('wonderland.lit')
10
+ File.open("#{dir}/wonderland.lit.crt", 'w') {|f| f.write(domain) }
11
+ File.open("#{dir}/wonderland.lit.key", 'w') {|f| f.write(key) }
12
+
13
+ wildcard, key = certificate('*.wonderland.lit')
14
+ File.open("#{dir}/wildcard.lit.crt", 'w') {|f| f.write(wildcard) }
15
+ File.open("#{dir}/wildcard.lit.key", 'w') {|f| f.write(key) }
16
+
17
+ @store = Vines::Store.new('conf/certs')
18
+ end
19
+
20
+ after do
21
+ %w[wonderland.lit.crt wonderland.lit.key wildcard.lit.crt wildcard.lit.key].each do |f|
22
+ name = "conf/certs/#{f}"
23
+ File.delete(name) if File.exists?(name)
24
+ end
25
+ end
26
+
27
+ it 'parses certificate files' do
28
+ refute @store.certs.empty?
29
+ assert_equal OpenSSL::X509::Certificate, @store.certs.first.class
30
+ end
31
+
32
+ it 'ignores expired certificates' do
33
+ assert @store.certs.all? {|c| c.not_after > Time.new }
34
+ end
35
+
36
+ describe 'files_for_domain' do
37
+ it 'handles invalid input' do
38
+ assert_nil @store.files_for_domain(nil)
39
+ assert_nil @store.files_for_domain('')
40
+ end
41
+
42
+ it 'finds files by name' do
43
+ refute_nil @store.files_for_domain('wonderland.lit')
44
+ cert, key = @store.files_for_domain('wonderland.lit')
45
+ assert_certificate_matches_key cert, key
46
+ assert_equal 'wonderland.lit.crt', File.basename(cert)
47
+ assert_equal 'wonderland.lit.key', File.basename(key)
48
+ end
49
+
50
+ it 'finds files for wildcard' do
51
+ refute_nil @store.files_for_domain('foo.wonderland.lit')
52
+ cert, key = @store.files_for_domain('foo.wonderland.lit')
53
+ assert_certificate_matches_key cert, key
54
+ assert_equal 'wildcard.lit.crt', File.basename(cert)
55
+ assert_equal 'wildcard.lit.key', File.basename(key)
56
+ end
57
+ end
58
+
59
+ describe 'domain?' do
60
+ it 'handles invalid input' do
61
+ cert, key = certificate('wonderland.lit')
62
+ refute @store.domain?(nil, nil)
63
+ refute @store.domain?(cert, nil)
64
+ refute @store.domain?(cert, '')
65
+ refute @store.domain?(nil, '')
66
+ assert @store.domain?(cert, 'wonderland.lit')
67
+ end
68
+
69
+ it 'verifies certificate subject domains' do
70
+ cert, key = certificate('wonderland.lit')
71
+ refute @store.domain?(cert, 'bogus')
72
+ refute @store.domain?(cert, 'www.wonderland.lit')
73
+ assert @store.domain?(cert, 'wonderland.lit')
74
+ end
75
+
76
+ it 'verifies certificate subject alt domains' do
77
+ cert, key = certificate('wonderland.lit', 'www.wonderland.lit')
78
+ refute @store.domain?(cert, 'bogus')
79
+ refute @store.domain?(cert, 'tea.wonderland.lit')
80
+ assert @store.domain?(cert, 'www.wonderland.lit')
81
+ assert @store.domain?(cert, 'wonderland.lit')
82
+ end
83
+
84
+ it 'verifies certificate wildcard domains' do
85
+ cert, key = certificate('wonderland.lit', '*.wonderland.lit')
86
+ refute @store.domain?(cert, 'bogus')
87
+ refute @store.domain?(cert, 'one.two.wonderland.lit')
88
+ assert @store.domain?(cert, 'tea.wonderland.lit')
89
+ assert @store.domain?(cert, 'www.wonderland.lit')
90
+ assert @store.domain?(cert, 'wonderland.lit')
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def assert_certificate_matches_key(cert, key)
97
+ refute_nil cert
98
+ refute_nil key
99
+ cert = OpenSSL::X509::Certificate.new(File.read(cert))
100
+ key = OpenSSL::PKey::RSA.new(File.read(key))
101
+ assert_equal cert.public_key.to_s, key.public_key.to_s
102
+ end
103
+
104
+ def certificate(domain, altname=nil)
105
+ # use small key so tests are fast
106
+ key = OpenSSL::PKey::RSA.generate(256)
107
+
108
+ name = OpenSSL::X509::Name.parse("/C=US/ST=Colorado/L=Denver/O=Test/CN=#{domain}")
109
+ cert = OpenSSL::X509::Certificate.new
110
+ cert.version = 2
111
+ cert.subject = name
112
+ cert.issuer = name
113
+ cert.serial = Time.now.to_i
114
+ cert.public_key = key.public_key
115
+ cert.not_before = Time.now
116
+ cert.not_after = Time.now + 3600
117
+
118
+ if altname
119
+ factory = OpenSSL::X509::ExtensionFactory.new
120
+ factory.subject_certificate = cert
121
+ factory.issuer_certificate = cert
122
+ cert.extensions = [
123
+ %w[subjectKeyIdentifier hash],
124
+ %w[subjectAltName] << [domain, altname].map {|n| "DNS:#{n}" }.join(',')
125
+ ].map {|k, v| factory.create_ext(k, v) }
126
+ end
127
+
128
+ [cert.to_pem, key.to_pem]
129
+ end
130
+ end
@@ -0,0 +1,137 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Client::Auth do
6
+ # disable logging for tests
7
+ Class.new.extend(Vines::Log).log.level = Logger::FATAL
8
+
9
+ class MockStorage < Vines::Storage
10
+ def initialize(raise_error=false)
11
+ @raise_error = raise_error
12
+ end
13
+
14
+ def authenticate(username, password)
15
+ username = username.to_s
16
+ raise 'temp auth fail' if @raise_error
17
+ user = Vines::User.new(jid: 'alice@wonderland.lit')
18
+ users = {'alice@wonderland.lit' => 'secr3t'}
19
+ (users.key?(username) && (users[username] == password)) ? user : nil
20
+ end
21
+
22
+ def find_user(jid)
23
+ end
24
+
25
+ def save_user(user)
26
+ end
27
+ end
28
+
29
+ subject { Vines::Stream::Client::Auth.new(stream) }
30
+ let(:stream) { MiniTest::Mock.new }
31
+
32
+ describe 'error handling' do
33
+ it 'rejects invalid element' do
34
+ node = node('<bogus/>')
35
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
36
+ end
37
+
38
+ it 'rejects invalid element in sasl namespace' do
39
+ node = node(%Q{<bogus xmlns="#{Vines::NAMESPACES[:sasl]}"/>})
40
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
41
+ end
42
+
43
+ it 'rejects auth elements missing sasl namespace' do
44
+ node = node('<auth/>')
45
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
46
+ end
47
+
48
+ it 'rejects auth element with invalid namespace' do
49
+ node = node('<auth xmlns="bogus"/>')
50
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
51
+ end
52
+
53
+ it 'rejects valid auth element missing mechanism' do
54
+ stream.expect :error, nil, [Vines::SaslErrors::InvalidMechanism]
55
+ stream.expect :authentication_mechanisms, ['PLAIN']
56
+ node = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}">tokens</auth>})
57
+ subject.node(node)
58
+ stream.verify
59
+ end
60
+
61
+ it 'rejects valid auth element with invalid mechanism' do
62
+ stream.expect :error, nil, [Vines::SaslErrors::InvalidMechanism]
63
+ stream.expect :authentication_mechanisms, ['PLAIN']
64
+ node = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="bogus">tokens</auth>})
65
+ subject.node(node)
66
+ stream.verify
67
+ end
68
+ end
69
+
70
+ describe 'plain auth' do
71
+ it 'rejects valid mechanism missing base64 text' do
72
+ stream.expect :error, nil, [Vines::SaslErrors::MalformedRequest]
73
+ node = plain('')
74
+ subject.node(node)
75
+ stream.verify
76
+ end
77
+
78
+ it 'rejects invalid base64 text' do
79
+ stream.expect :error, nil, [Vines::SaslErrors::IncorrectEncoding]
80
+ stream.expect :authentication_mechanisms, ['PLAIN']
81
+ node = plain('tokens')
82
+ subject.node(node)
83
+ stream.verify
84
+ end
85
+
86
+ it 'rejects invalid password' do
87
+ stream.expect :storage, MockStorage.new
88
+ stream.expect :domain, 'wonderland.lit'
89
+ stream.expect :error, nil, [Vines::SaslErrors::NotAuthorized]
90
+ stream.expect :authentication_mechanisms, ['PLAIN']
91
+ node = plain(Base64.strict_encode64("\x00alice\x00bogus"))
92
+ subject.node(node)
93
+ stream.verify
94
+ end
95
+
96
+ it 'passes with valid password' do
97
+ user = Vines::User.new(jid: 'alice@wonderland.lit')
98
+ stream.expect :reset, nil
99
+ stream.expect :domain, 'wonderland.lit'
100
+ stream.expect :storage, MockStorage.new
101
+ stream.expect :user=, nil, [user]
102
+ stream.expect :write, nil, [%Q{<success xmlns="#{Vines::NAMESPACES[:sasl]}"/>}]
103
+ stream.expect :advance, nil, [Vines::Stream::Client::BindRestart]
104
+ stream.expect :authentication_mechanisms, ['PLAIN']
105
+ node = plain(Base64.strict_encode64("\x00alice\x00secr3t"))
106
+ subject.node(node)
107
+ stream.verify
108
+ end
109
+
110
+ it 'raises policy-violation after max auth attempts is reached' do
111
+ stream.expect :domain, 'wonderland.lit'
112
+ stream.expect :storage, MockStorage.new
113
+ node = -> { plain(Base64.strict_encode64("\x00alice\x00bogus")) }
114
+
115
+ stream.expect :authentication_mechanisms, ['PLAIN']
116
+ stream.expect :error, nil, [Vines::SaslErrors::NotAuthorized]
117
+ subject.node(node.call)
118
+ stream.verify
119
+
120
+ stream.expect :authentication_mechanisms, ['PLAIN']
121
+ stream.expect :error, nil, [Vines::SaslErrors::NotAuthorized]
122
+ subject.node(node.call)
123
+ stream.verify
124
+
125
+ stream.expect :authentication_mechanisms, ['PLAIN']
126
+ stream.expect :error, nil, [Vines::StreamErrors::PolicyViolation]
127
+ subject.node(node.call)
128
+ stream.verify
129
+ end
130
+ end
131
+
132
+ private
133
+
134
+ def plain(authzid)
135
+ node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="PLAIN">#{authzid}</auth>})
136
+ end
137
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Client::Ready do
6
+ STANZAS = []
7
+
8
+ before do
9
+ @stream = MiniTest::Mock.new
10
+ @state = Vines::Stream::Client::Ready.new(@stream, nil)
11
+ def @state.to_stanza(node)
12
+ if node.name == 'bogus'
13
+ nil
14
+ else
15
+ stanza = MiniTest::Mock.new
16
+ stanza.expect(:process, nil)
17
+ stanza.expect(:validate_to, nil)
18
+ stanza.expect(:validate_from, nil)
19
+ STANZAS << stanza
20
+ stanza
21
+ end
22
+ end
23
+ end
24
+
25
+ after do
26
+ STANZAS.clear
27
+ end
28
+
29
+ it 'processes a valid node' do
30
+ node = node('<message/>')
31
+ @state.node(node)
32
+ assert_equal 1, STANZAS.size
33
+ assert STANZAS.map {|s| s.verify }.all?
34
+ end
35
+
36
+ it 'raises an unsupported-stanza-type stream error for invalid node' do
37
+ node = node('<bogus/>')
38
+ assert_raises(Vines::StreamErrors::UnsupportedStanzaType) { @state.node(node) }
39
+ assert STANZAS.empty?
40
+ end
41
+
42
+ private
43
+
44
+ def node(xml)
45
+ Nokogiri::XML(xml).root
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Client::Session do
6
+ subject { Vines::Stream::Client::Session.new(stream) }
7
+ let(:another) { Vines::Stream::Client::Session.new(stream) }
8
+ let(:stream) { OpenStruct.new(config: nil) }
9
+
10
+ describe 'session equality checks' do
11
+ it 'uses class in equality check' do
12
+ (subject <=> 42).must_be_nil
13
+ end
14
+
15
+ it 'is equal to itself' do
16
+ assert subject == subject
17
+ assert subject.eql?(subject)
18
+ assert subject.hash == subject.hash
19
+ end
20
+
21
+ it 'is not equal to another session' do
22
+ refute subject == another
23
+ refute subject.eql?(another)
24
+ refute subject.hash == another.hash
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Component::Handshake do
6
+ subject { Vines::Stream::Component::Handshake.new(stream) }
7
+ let(:stream) { MiniTest::Mock.new }
8
+
9
+ describe 'when invalid element is received' do
10
+ it 'raises a not-authorized stream error' do
11
+ node = node('<message/>')
12
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
13
+ end
14
+ end
15
+
16
+ describe 'when handshake with no text is received' do
17
+ it 'raises a not-authorized stream error' do
18
+ stream.expect :secret, 'secr3t'
19
+ node = node('<handshake/>')
20
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
21
+ stream.verify
22
+ end
23
+ end
24
+
25
+ describe 'when handshake with invalid secret is received' do
26
+ it 'raises a not-authorized stream error' do
27
+ stream.expect :secret, 'secr3t'
28
+ node = node('<handshake>bogus</handshake>')
29
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
30
+ stream.verify
31
+ end
32
+ end
33
+
34
+ describe 'when good handshake is received' do
35
+ let(:router) { MiniTest::Mock.new }
36
+
37
+ before do
38
+ router.expect :<<, nil, [stream]
39
+ stream.expect :router, router
40
+ stream.expect :secret, 'secr3t'
41
+ stream.expect :write, nil, ['<handshake/>']
42
+ stream.expect :advance, nil, [Vines::Stream::Component::Ready.new(stream)]
43
+ end
44
+
45
+ it 'completes the handshake and advances the stream into the ready state' do
46
+ node = node('<handshake>secr3t</handshake>')
47
+ subject.node(node)
48
+ stream.verify
49
+ router.verify
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,103 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Component::Ready do
6
+ subject { Vines::Stream::Component::Ready.new(stream, nil) }
7
+ let(:alice) { Vines::User.new(jid: 'alice@tea.wonderland.lit') }
8
+ let(:hatter) { Vines::User.new(jid: 'hatter@wonderland.lit') }
9
+ let(:stream) { MiniTest::Mock.new }
10
+ let(:config) do
11
+ Vines::Config.new do
12
+ host 'wonderland.lit' do
13
+ storage(:fs) { dir Dir.tmpdir }
14
+ end
15
+ end
16
+ end
17
+
18
+ before do
19
+ class << stream
20
+ attr_accessor :config
21
+ end
22
+ stream.config = config
23
+ end
24
+
25
+ describe 'when missing to and from addresses' do
26
+ it 'raises an improper-addressing stream error' do
27
+ node = node('<message/>')
28
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::ImproperAddressing
29
+ stream.verify
30
+ end
31
+ end
32
+
33
+ describe 'when missing from address' do
34
+ it 'raises an improper-addressing stream error' do
35
+ node = node(%q{<message to="hatter@wonderland.lit"/>})
36
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::ImproperAddressing
37
+ stream.verify
38
+ end
39
+ end
40
+
41
+ describe 'when missing to address' do
42
+ it 'raises an improper-addressing stream error' do
43
+ node = node(%q{<message from="alice@tea.wonderland.lit"/>})
44
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::ImproperAddressing
45
+ stream.verify
46
+ end
47
+ end
48
+
49
+ describe 'when from address domain does not match component domain' do
50
+ it 'raises and invalid-from stream error' do
51
+ stream.expect :remote_domain, 'tea.wonderland.lit'
52
+ node = node(%q{<message from="alice@bogus.wonderland.lit" to="hatter@wonderland.lit"/>})
53
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::InvalidFrom
54
+ stream.verify
55
+ end
56
+ end
57
+
58
+ describe 'when unrecognized element is received' do
59
+ it 'raises an unsupported-stanza-type stream error' do
60
+ node = node('<bogus/>')
61
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::UnsupportedStanzaType
62
+ stream.verify
63
+ end
64
+ end
65
+
66
+ describe 'when addressed to a remote jid' do
67
+ let(:router) { MiniTest::Mock.new }
68
+ let(:xml) { node(%q{<message from="alice@tea.wonderland.lit" to="romeo@verona.lit"/>}) }
69
+
70
+ before do
71
+ router.expect :route, nil, [xml]
72
+ stream.expect :remote_domain, 'tea.wonderland.lit'
73
+ stream.expect :user=, nil, [alice]
74
+ stream.expect :router, router
75
+ end
76
+
77
+ it 'routes rather than handle locally' do
78
+ subject.node(xml)
79
+ stream.verify
80
+ router.verify
81
+ end
82
+ end
83
+
84
+ describe 'when addressed to a local jid' do
85
+ let(:recipient) { MiniTest::Mock.new }
86
+ let(:xml) { node(%q{<message from="alice@tea.wonderland.lit" to="hatter@wonderland.lit"/>}) }
87
+
88
+ before do
89
+ recipient.expect :user, hatter
90
+ recipient.expect :write, nil, [xml]
91
+ stream.expect :remote_domain, 'tea.wonderland.lit'
92
+ stream.expect :user=, nil, [alice]
93
+ stream.expect :user, alice
94
+ stream.expect :connected_resources, [recipient], [hatter.jid]
95
+ end
96
+
97
+ it 'sends the message to the connected stream' do
98
+ subject.node(xml)
99
+ stream.verify
100
+ recipient.verify
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Component::Start do
6
+ before do
7
+ @stream = MiniTest::Mock.new
8
+ @state = Vines::Stream::Component::Start.new(@stream)
9
+ end
10
+
11
+ it 'raises not-authorized stream error for invalid element' do
12
+ node = node('<message/>')
13
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
14
+ end
15
+
16
+ it 'raises not-authorized stream error for missing stream namespace' do
17
+ node = node('<stream:stream/>')
18
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
19
+ end
20
+
21
+ it 'raises not-authorized stream error for invalid stream namespace' do
22
+ node = node('<stream:stream xmlns="bogus"/>')
23
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
24
+ end
25
+
26
+ it 'advances the state machine for valid stream header' do
27
+ node = node(%q{<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:component:accept" to="tea.wonderland.lit"/>})
28
+ @stream.expect(:start, nil, [node])
29
+ @stream.expect(:advance, nil, [Vines::Stream::Component::Handshake.new(@stream)])
30
+ @state.node(node)
31
+ assert @stream.verify
32
+ end
33
+
34
+ private
35
+
36
+ def node(xml)
37
+ Nokogiri::XML(xml).root
38
+ end
39
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Http::Auth do
6
+ before do
7
+ @stream = MiniTest::Mock.new
8
+ @state = Vines::Stream::Http::Auth.new(@stream, nil)
9
+ end
10
+
11
+ def test_missing_body_raises_error
12
+ node = node('<presence type="unavailable"/>')
13
+ @stream.expect(:valid_session?, true, [nil])
14
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
15
+ end
16
+
17
+ def test_body_with_missing_namespace_raises_error
18
+ node = node('<body rid="42" sid="12"/>')
19
+ @stream.expect(:valid_session?, true, ['12'])
20
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
21
+ end
22
+
23
+ def test_missing_rid_raises_error
24
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" sid="12"/>')
25
+ @stream.expect(:valid_session?, true, ['12'])
26
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
27
+ end
28
+
29
+ def test_invalid_session_raises_error
30
+ @stream.expect(:valid_session?, false, ['12'])
31
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
32
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
33
+ assert @stream.verify
34
+ end
35
+
36
+ def test_empty_body_raises_error
37
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
38
+ @stream.expect(:valid_session?, true, ['12'])
39
+ @stream.expect(:parse_body, [], [node])
40
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
41
+ assert @stream.verify
42
+ end
43
+
44
+ def test_body_with_two_children_raises_error
45
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"><message/><message/></body>')
46
+ message = node('<message/>')
47
+ @stream.expect(:valid_session?, true, ['12'])
48
+ @stream.expect(:parse_body, [message, message], [node])
49
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
50
+ assert @stream.verify
51
+ end
52
+
53
+ def test_valid_body_processes
54
+ auth = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="PLAIN"/>})
55
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"></body>')
56
+ node << auth
57
+ @stream.expect(:valid_session?, true, ['12'])
58
+ @stream.expect(:parse_body, [auth], [node])
59
+ # this error means we correctly called the parent method Client#node
60
+ @stream.expect(:error, nil, [Vines::SaslErrors::MalformedRequest.new])
61
+ @state.node(node)
62
+ assert @stream.verify
63
+ end
64
+
65
+ private
66
+
67
+ def node(xml)
68
+ Nokogiri::XML(xml).root
69
+ end
70
+ end