vines 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. data/LICENSE +19 -0
  2. data/README +34 -0
  3. data/Rakefile +55 -0
  4. data/bin/vines +95 -0
  5. data/conf/certs/README +32 -0
  6. data/conf/certs/ca-bundle.crt +3987 -0
  7. data/conf/config.rb +114 -0
  8. data/lib/vines.rb +155 -0
  9. data/lib/vines/command/bcrypt.rb +12 -0
  10. data/lib/vines/command/cert.rb +49 -0
  11. data/lib/vines/command/init.rb +58 -0
  12. data/lib/vines/command/ldap.rb +35 -0
  13. data/lib/vines/command/restart.rb +12 -0
  14. data/lib/vines/command/schema.rb +24 -0
  15. data/lib/vines/command/start.rb +28 -0
  16. data/lib/vines/command/stop.rb +18 -0
  17. data/lib/vines/config.rb +191 -0
  18. data/lib/vines/contact.rb +99 -0
  19. data/lib/vines/daemon.rb +78 -0
  20. data/lib/vines/error.rb +150 -0
  21. data/lib/vines/jid.rb +56 -0
  22. data/lib/vines/kit.rb +23 -0
  23. data/lib/vines/router.rb +125 -0
  24. data/lib/vines/stanza.rb +55 -0
  25. data/lib/vines/stanza/iq.rb +50 -0
  26. data/lib/vines/stanza/iq/auth.rb +18 -0
  27. data/lib/vines/stanza/iq/disco_info.rb +25 -0
  28. data/lib/vines/stanza/iq/disco_items.rb +23 -0
  29. data/lib/vines/stanza/iq/error.rb +16 -0
  30. data/lib/vines/stanza/iq/ping.rb +16 -0
  31. data/lib/vines/stanza/iq/query.rb +10 -0
  32. data/lib/vines/stanza/iq/result.rb +16 -0
  33. data/lib/vines/stanza/iq/roster.rb +153 -0
  34. data/lib/vines/stanza/iq/session.rb +22 -0
  35. data/lib/vines/stanza/iq/vcard.rb +58 -0
  36. data/lib/vines/stanza/message.rb +41 -0
  37. data/lib/vines/stanza/presence.rb +119 -0
  38. data/lib/vines/stanza/presence/error.rb +23 -0
  39. data/lib/vines/stanza/presence/probe.rb +38 -0
  40. data/lib/vines/stanza/presence/subscribe.rb +66 -0
  41. data/lib/vines/stanza/presence/subscribed.rb +64 -0
  42. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  43. data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
  44. data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
  45. data/lib/vines/storage.rb +216 -0
  46. data/lib/vines/storage/couchdb.rb +119 -0
  47. data/lib/vines/storage/ldap.rb +59 -0
  48. data/lib/vines/storage/local.rb +66 -0
  49. data/lib/vines/storage/redis.rb +108 -0
  50. data/lib/vines/storage/sql.rb +174 -0
  51. data/lib/vines/store.rb +51 -0
  52. data/lib/vines/stream.rb +198 -0
  53. data/lib/vines/stream/client.rb +131 -0
  54. data/lib/vines/stream/client/auth.rb +94 -0
  55. data/lib/vines/stream/client/auth_restart.rb +33 -0
  56. data/lib/vines/stream/client/bind.rb +58 -0
  57. data/lib/vines/stream/client/bind_restart.rb +25 -0
  58. data/lib/vines/stream/client/closed.rb +13 -0
  59. data/lib/vines/stream/client/ready.rb +15 -0
  60. data/lib/vines/stream/client/start.rb +27 -0
  61. data/lib/vines/stream/client/tls.rb +37 -0
  62. data/lib/vines/stream/component.rb +53 -0
  63. data/lib/vines/stream/component/handshake.rb +25 -0
  64. data/lib/vines/stream/component/ready.rb +24 -0
  65. data/lib/vines/stream/component/start.rb +19 -0
  66. data/lib/vines/stream/http.rb +111 -0
  67. data/lib/vines/stream/http/http_request.rb +22 -0
  68. data/lib/vines/stream/http/http_state.rb +139 -0
  69. data/lib/vines/stream/http/http_states.rb +53 -0
  70. data/lib/vines/stream/parser.rb +78 -0
  71. data/lib/vines/stream/server.rb +126 -0
  72. data/lib/vines/stream/server/auth.rb +13 -0
  73. data/lib/vines/stream/server/auth_restart.rb +19 -0
  74. data/lib/vines/stream/server/final_restart.rb +20 -0
  75. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  76. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  77. data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
  78. data/lib/vines/stream/server/outbound/final_features.rb +27 -0
  79. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  80. data/lib/vines/stream/server/outbound/start.rb +20 -0
  81. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  82. data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
  83. data/lib/vines/stream/server/ready.rb +20 -0
  84. data/lib/vines/stream/server/start.rb +13 -0
  85. data/lib/vines/stream/server/tls.rb +13 -0
  86. data/lib/vines/stream/state.rb +55 -0
  87. data/lib/vines/token_bucket.rb +46 -0
  88. data/lib/vines/user.rb +124 -0
  89. data/lib/vines/version.rb +5 -0
  90. data/lib/vines/xmpp_server.rb +25 -0
  91. data/test/config_test.rb +396 -0
  92. data/test/error_test.rb +59 -0
  93. data/test/ext/nokogiri.rb +14 -0
  94. data/test/jid_test.rb +71 -0
  95. data/test/kit_test.rb +21 -0
  96. data/test/router_test.rb +60 -0
  97. data/test/stanza/iq/roster_test.rb +198 -0
  98. data/test/stanza/iq/session_test.rb +30 -0
  99. data/test/stanza/iq/vcard_test.rb +159 -0
  100. data/test/stanza/message_test.rb +124 -0
  101. data/test/stanza/presence/subscribe_test.rb +75 -0
  102. data/test/storage/couchdb_test.rb +102 -0
  103. data/test/storage/ldap_test.rb +207 -0
  104. data/test/storage/local_test.rb +54 -0
  105. data/test/storage/redis_test.rb +75 -0
  106. data/test/storage/sql_test.rb +55 -0
  107. data/test/storage/storage_tests.rb +134 -0
  108. data/test/storage_test.rb +90 -0
  109. data/test/stream/client/auth_test.rb +127 -0
  110. data/test/stream/client/ready_test.rb +47 -0
  111. data/test/stream/component/handshake_test.rb +46 -0
  112. data/test/stream/component/ready_test.rb +105 -0
  113. data/test/stream/component/start_test.rb +41 -0
  114. data/test/stream/parser_test.rb +121 -0
  115. data/test/stream/server/outbound/auth_test.rb +77 -0
  116. data/test/stream/server/ready_test.rb +100 -0
  117. data/test/token_bucket_test.rb +24 -0
  118. data/test/user_test.rb +64 -0
  119. metadata +318 -0
@@ -0,0 +1,54 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'storage_tests'
4
+ require 'vines'
5
+ require 'test/unit'
6
+
7
+ class LocalTest < Test::Unit::TestCase
8
+ include StorageTests
9
+
10
+ def setup
11
+ @files = {
12
+ :empty => './empty@wonderland.lit.user',
13
+ :no_pass => './no_password@wonderland.lit.user',
14
+ :clear_pass => './clear_password@wonderland.lit.user',
15
+ :bcrypt => './bcrypt_password@wonderland.lit.user',
16
+ :full => './full@wonderland.lit.user',
17
+ :vcard => './full@wonderland.lit.vcard'
18
+ }
19
+ File.open(@files[:empty], 'w') {|f| f.write('') }
20
+ File.open(@files[:no_pass], 'w') {|f| f.write('foo: bar') }
21
+ File.open(@files[:clear_pass], 'w') {|f| f.write('password: secret') }
22
+ File.open(@files[:bcrypt], 'w') {|f| f.write("password: #{BCrypt::Password.create('secret')}") }
23
+ File.open(@files[:full], 'w') do |f|
24
+ f.puts("password: #{BCrypt::Password.create('secret')}")
25
+ f.puts("name: Tester")
26
+ f.puts("roster:")
27
+ f.puts(" contact1@wonderland.lit:")
28
+ f.puts(" name: Contact1")
29
+ f.puts(" groups: [Group1, Group2]")
30
+ f.puts(" contact2@wonderland.lit:")
31
+ f.puts(" name: Contact2")
32
+ f.puts(" groups: [Group3, Group4]")
33
+ end
34
+ File.open(@files[:vcard], 'w') {|f| f.write(StorageTests::VCARD.to_xml) }
35
+ end
36
+
37
+ def teardown
38
+ misc = %w[user vcard].map {|ext| "./save_user@domain.tld.#{ext}" }
39
+ [*misc, *@files.values].each do |f|
40
+ File.delete(f) if File.exist?(f)
41
+ end
42
+ end
43
+
44
+ def storage
45
+ Vines::Storage::Local.new { dir '.' }
46
+ end
47
+
48
+ def test_init
49
+ assert_raise(RuntimeError) { Vines::Storage::Local.new {} }
50
+ assert_raise(RuntimeError) { Vines::Storage::Local.new { dir 'bogus' } }
51
+ assert_raise(RuntimeError) { Vines::Storage::Local.new { dir '/sbin' } }
52
+ assert_nothing_raised { Vines::Storage::Local.new { dir '.' } }
53
+ end
54
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'storage_tests'
4
+ require 'vines'
5
+ require 'test/unit'
6
+
7
+ class RedisTest < Test::Unit::TestCase
8
+ include StorageTests
9
+
10
+ MOCK_REDIS = Class.new do
11
+ def initialize
12
+ @db = {}
13
+ end
14
+ def get(key)
15
+ EM.next_tick { yield @db[key] }
16
+ end
17
+ def hgetall(key)
18
+ EM.next_tick { yield @db[key] || {} }
19
+ end
20
+ def set(key, value)
21
+ @db[key] = value
22
+ EM.next_tick { yield if block_given? }
23
+ end
24
+ def hmset(key, *args)
25
+ @db[key] = Hash[*args]
26
+ EM.next_tick { yield if block_given? }
27
+ end
28
+ def del(key)
29
+ @db.delete(key)
30
+ EM.next_tick { yield if block_given? }
31
+ end
32
+ def flushdb
33
+ @db.clear
34
+ EM.next_tick { yield if block_given? }
35
+ end
36
+ end.new
37
+
38
+ def setup
39
+ EMLoop.new do
40
+ db = MOCK_REDIS
41
+ db.set('user:empty@wonderland.lit', {}.to_json)
42
+ db.set('user:no_password@wonderland.lit', {'foo' => 'bar'}.to_json)
43
+ db.set('user:clear_password@wonderland.lit', {'password' => 'secret'}.to_json)
44
+ db.set('user:bcrypt_password@wonderland.lit', {'password' => BCrypt::Password.create('secret')}.to_json)
45
+ db.set('user:full@wonderland.lit', {
46
+ 'password' => BCrypt::Password.create('secret'),
47
+ 'name' => 'Tester'
48
+ }.to_json)
49
+ db.hmset('roster:full@wonderland.lit',
50
+ 'contact1@wonderland.lit',
51
+ {'name' => 'Contact1', 'groups' => %w[Group1 Group2]}.to_json,
52
+ 'contact2@wonderland.lit',
53
+ {'name' => 'Contact2', 'groups' => %w[Group3 Group4]}.to_json)
54
+ db.set('vcard:full@wonderland.lit', {'card' => StorageTests::VCARD.to_xml}.to_json)
55
+ end
56
+ end
57
+
58
+ def teardown
59
+ MOCK_REDIS.flushdb
60
+ end
61
+
62
+ def storage
63
+ storage = Vines::Storage::Redis.new { host 'localhost'; port 6397 }
64
+ def storage.redis; RedisTest::MOCK_REDIS; end
65
+ storage
66
+ end
67
+
68
+ def test_init
69
+ EMLoop.new do
70
+ assert_nothing_raised { Vines::Storage::Redis.new {} }
71
+ assert_nothing_raised { Vines::Storage::Redis.new { host 'localhost' } }
72
+ assert_nothing_raised { Vines::Storage::Redis.new { host'localhost'; port '6379' } }
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,55 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'storage_tests'
4
+ require 'vines'
5
+ require 'test/unit'
6
+
7
+ class SqlTest < Test::Unit::TestCase
8
+ include StorageTests
9
+
10
+ DB_FILE = "./xmpp_testcase.db"
11
+ ActiveRecord::Migration.verbose = false
12
+
13
+ def setup
14
+ storage.create_schema(:force => true)
15
+ Vines::Storage::Sql::User.new(:jid => 'empty@wonderland.lit', :name => '', :password => '').save
16
+ Vines::Storage::Sql::User.new(:jid => 'no_password@wonderland.lit', :name => '', :password => '').save
17
+ Vines::Storage::Sql::User.new(:jid => 'clear_password@wonderland.lit', :name => '',
18
+ :password => 'secret').save
19
+ Vines::Storage::Sql::User.new(:jid => 'bcrypt_password@wonderland.lit', :name => '',
20
+ :password => BCrypt::Password.create('secret')).save
21
+ groups = %w[Group1 Group2 Group3 Group4].map do |name|
22
+ Vines::Storage::Sql::Group.find_or_create_by_name(name)
23
+ end
24
+ full = Vines::Storage::Sql::User.new(
25
+ :jid => 'full@wonderland.lit',
26
+ :name => 'Tester',
27
+ :password => BCrypt::Password.create('secret'),
28
+ :vcard => StorageTests::VCARD.to_xml)
29
+ full.contacts << Vines::Storage::Sql::Contact.new(
30
+ :jid => 'contact1@wonderland.lit',
31
+ :name => 'Contact1',
32
+ :groups => groups[0, 2],
33
+ :subscription => 'both')
34
+ full.contacts << Vines::Storage::Sql::Contact.new(
35
+ :jid => 'contact2@wonderland.lit',
36
+ :name => 'Contact2',
37
+ :groups => groups[2, 2],
38
+ :subscription => 'both')
39
+ full.save
40
+ end
41
+
42
+ def teardown
43
+ File.delete(DB_FILE) if File.exist?(DB_FILE)
44
+ end
45
+
46
+ def storage
47
+ Vines::Storage::Sql.new { adapter 'sqlite3'; database DB_FILE }
48
+ end
49
+
50
+ def test_init
51
+ assert_raise(RuntimeError) { Vines::Storage::Sql.new {} }
52
+ assert_raise(RuntimeError) { Vines::Storage::Sql.new { adapter 'postgresql' } }
53
+ assert_nothing_raised { Vines::Storage::Sql.new { adapter 'sqlite3'; database ':memory:' } }
54
+ end
55
+ end
@@ -0,0 +1,134 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'ext/nokogiri'
5
+ require 'test/unit'
6
+
7
+ # Mixin methods for storage implementation test classes. The behavioral
8
+ # tests are the same regardless of implementation so share those methods
9
+ # here.
10
+ module StorageTests
11
+ VCARD = Nokogiri::XML(%q{
12
+ <vCard xmlns="vcard-temp">
13
+ <FN>Alice in Wonderland</FN>
14
+ </vCard>
15
+ }.strip).root
16
+
17
+ class EMLoop
18
+ def initialize
19
+ EM.run do
20
+ Fiber.new do
21
+ yield
22
+ EM.stop
23
+ end.resume
24
+ end
25
+ end
26
+ end
27
+
28
+ def test_authenticate
29
+ EMLoop.new do
30
+ db = storage
31
+ assert_nil db.authenticate(nil, nil)
32
+ assert_nil db.authenticate(nil, 'secret')
33
+ assert_nil db.authenticate('bogus', nil)
34
+ assert_nil db.authenticate('bogus', 'secret')
35
+ assert_nil db.authenticate('empty@wonderland.lit', 'secret')
36
+ assert_nil db.authenticate('no_password@wonderland.lit', 'secret')
37
+ assert_nil db.authenticate('clear_password@wonderland.lit', 'secret')
38
+
39
+ user = db.authenticate('bcrypt_password@wonderland.lit', 'secret')
40
+ assert_not_nil user
41
+ assert_equal('bcrypt_password@wonderland.lit', user.jid.to_s)
42
+
43
+ user = db.authenticate('full@wonderland.lit', 'secret')
44
+ assert_not_nil user
45
+ assert_equal 'Tester', user.name
46
+ assert_equal 'full@wonderland.lit', user.jid.to_s
47
+
48
+ assert_equal 2, user.roster.length
49
+ assert_equal 'contact1@wonderland.lit', user.roster[0].jid.to_s
50
+ assert_equal 'Contact1', user.roster[0].name
51
+ assert_equal 2, user.roster[0].groups.length
52
+ assert_equal 'Group1', user.roster[0].groups[0]
53
+ assert_equal 'Group2', user.roster[0].groups[1]
54
+
55
+ assert_equal 'contact2@wonderland.lit', user.roster[1].jid.to_s
56
+ assert_equal 'Contact2', user.roster[1].name
57
+ assert_equal 2, user.roster[1].groups.length
58
+ assert_equal 'Group3', user.roster[1].groups[0]
59
+ assert_equal 'Group4', user.roster[1].groups[1]
60
+ end
61
+ end
62
+
63
+ def test_find_user
64
+ EMLoop.new do
65
+ db = storage
66
+ user = db.find_user(nil)
67
+ assert_nil user
68
+
69
+ user = db.find_user('full@wonderland.lit')
70
+ assert_not_nil user
71
+ assert_equal 'full@wonderland.lit', user.jid.to_s
72
+
73
+ user = db.find_user(Vines::JID.new('full@wonderland.lit'))
74
+ assert_not_nil user
75
+ assert_equal 'full@wonderland.lit', user.jid.to_s
76
+
77
+ user = db.find_user(Vines::JID.new('full@wonderland.lit/resource'))
78
+ assert_not_nil user
79
+ assert_equal 'full@wonderland.lit', user.jid.to_s
80
+ end
81
+ end
82
+
83
+ def test_save_user
84
+ EMLoop.new do
85
+ db = storage
86
+ user = Vines::User.new(
87
+ :jid => 'save_user@domain.tld/resource1',
88
+ :name => 'Save User',
89
+ :password => 'secret')
90
+ user.roster << Vines::Contact.new(
91
+ :jid => 'contact1@domain.tld/resource2',
92
+ :name => 'Contact 1')
93
+ db.save_user(user)
94
+ user = db.find_user('save_user@domain.tld')
95
+ assert_not_nil user
96
+ assert_equal 'save_user@domain.tld', user.jid.to_s
97
+ assert_equal 'Save User', user.name
98
+ assert_equal 1, user.roster.length
99
+ assert_equal 'contact1@domain.tld', user.roster[0].jid.to_s
100
+ assert_equal 'Contact 1', user.roster[0].name
101
+ end
102
+ end
103
+
104
+ def test_find_vcard
105
+ EMLoop.new do
106
+ db = storage
107
+ card = db.find_vcard(nil)
108
+ assert_nil card
109
+
110
+ card = db.find_vcard('full@wonderland.lit')
111
+ assert_not_nil card
112
+ assert_equal VCARD, card
113
+
114
+ card = db.find_vcard(Vines::JID.new('full@wonderland.lit'))
115
+ assert_not_nil card
116
+ assert_equal VCARD, card
117
+
118
+ card = db.find_vcard(Vines::JID.new('full@wonderland.lit/resource'))
119
+ assert_not_nil card
120
+ assert_equal VCARD, card
121
+ end
122
+ end
123
+
124
+ def test_save_vcard
125
+ EMLoop.new do
126
+ db = storage
127
+ db.save_user(Vines::User.new(:jid => 'save_user@domain.tld'))
128
+ db.save_vcard('save_user@domain.tld/resource1', VCARD)
129
+ card = db.find_vcard('save_user@domain.tld')
130
+ assert_not_nil card
131
+ assert_equal VCARD, card
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,90 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'storage_tests'
4
+ require 'vines'
5
+ require 'minitest/mock'
6
+ require 'test/unit'
7
+
8
+ class StorageTest < Test::Unit::TestCase
9
+ ALICE = 'alice@wonderland.lit'.freeze
10
+
11
+ class MockLdapStorage < Vines::Storage
12
+ attr_reader :authenticate_calls, :find_user_calls, :save_user_calls
13
+
14
+ def initialize(found_user=nil)
15
+ @found_user = found_user
16
+ @authenticate_calls = @find_user_calls = @save_user_calls = 0
17
+ @ldap = MiniTest::Mock.new
18
+ end
19
+
20
+ def authenticate(username, password)
21
+ @authenticate_calls += 1
22
+ nil
23
+ end
24
+ wrap_ldap :authenticate
25
+
26
+ def find_user(jid)
27
+ @find_user_calls += 1
28
+ @found_user
29
+ end
30
+
31
+ def save_user(user)
32
+ @save_user_calls += 1
33
+ end
34
+ end
35
+
36
+ def test_authenticate_with_ldap_missing_password
37
+ StorageTests::EMLoop.new do
38
+ storage = MockLdapStorage.new
39
+ user = storage.authenticate(ALICE, '')
40
+ assert_nil user
41
+ assert_equal 0, storage.authenticate_calls
42
+ assert_equal 0, storage.find_user_calls
43
+ assert_equal 0, storage.save_user_calls
44
+ assert storage.ldap.verify
45
+ end
46
+ end
47
+
48
+ def test_authenticate_with_ldap_bad_password
49
+ StorageTests::EMLoop.new do
50
+ storage = MockLdapStorage.new
51
+ storage.ldap.expect(:authenticate, nil, [ALICE, 'bogus'])
52
+ user = storage.authenticate(ALICE, 'bogus')
53
+ assert_nil user
54
+ assert_equal 0, storage.authenticate_calls
55
+ assert_equal 0, storage.find_user_calls
56
+ assert_equal 0, storage.save_user_calls
57
+ assert storage.ldap.verify
58
+ end
59
+ end
60
+
61
+ def test_authenticate_with_ldap_user_exists_in_database
62
+ StorageTests::EMLoop.new do
63
+ alice = Vines::User.new(:jid => ALICE)
64
+ storage = MockLdapStorage.new(alice)
65
+ storage.ldap.expect(:authenticate, alice, [ALICE, 'secr3t'])
66
+ user = storage.authenticate(ALICE, 'secr3t')
67
+ assert_not_nil user
68
+ assert_equal ALICE, user.jid.to_s
69
+ assert_equal 0, storage.authenticate_calls
70
+ assert_equal 1, storage.find_user_calls
71
+ assert_equal 0, storage.save_user_calls
72
+ assert storage.ldap.verify
73
+ end
74
+ end
75
+
76
+ def test_authenticate_with_ldap_save_user_to_database
77
+ StorageTests::EMLoop.new do
78
+ alice = Vines::User.new(:jid => ALICE)
79
+ storage = MockLdapStorage.new
80
+ storage.ldap.expect(:authenticate, alice, [ALICE, 'secr3t'])
81
+ user = storage.authenticate(ALICE, 'secr3t')
82
+ assert_not_nil user
83
+ assert_equal ALICE, user.jid.to_s
84
+ assert_equal 0, storage.authenticate_calls
85
+ assert_equal 1, storage.find_user_calls
86
+ assert_equal 1, storage.save_user_calls
87
+ assert storage.ldap.verify
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,127 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/mock'
5
+ require 'test/unit'
6
+
7
+ class ClientAuthTest < Test::Unit::TestCase
8
+ # disable logging for tests
9
+ Class.new.extend(Vines::Log).log.level = Logger::FATAL
10
+
11
+ class MockStorage < Vines::Storage
12
+ def initialize(raise_error=false)
13
+ @raise_error = raise_error
14
+ end
15
+
16
+ def authenticate(username, password)
17
+ raise 'temp auth fail' if @raise_error
18
+ user = Vines::User.new(:jid => 'alice@wonderland.lit')
19
+ users = {'alice@wonderland.lit' => 'secr3t'}
20
+ (users.key?(username) && (users[username] == password)) ? user : nil
21
+ end
22
+
23
+ def find_user(jid)
24
+ end
25
+
26
+ def save_user(user)
27
+ end
28
+ end
29
+
30
+ def setup
31
+ @stream = MiniTest::Mock.new
32
+ @state = Vines::Stream::Client::Auth.new(@stream)
33
+ end
34
+
35
+ def test_invalid_element
36
+ node = node('<bogus/>')
37
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
38
+ end
39
+
40
+ def test_invalid_sasl_element
41
+ node = node(%Q{<bogus xmlns="#{Vines::NAMESPACES[:sasl]}"/>})
42
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
43
+ end
44
+
45
+ def test_missing_namespace
46
+ node = node('<auth/>')
47
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
48
+ end
49
+
50
+ def test_invalid_namespace
51
+ node = node('<auth xmlns="bogus"/>')
52
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
53
+ end
54
+
55
+ def test_missing_mechanism
56
+ @stream.expect(:error, nil, [Vines::SaslErrors::InvalidMechanism.new])
57
+ node = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}">tokens</auth>})
58
+ assert_nothing_raised { @state.node(node) }
59
+ assert @stream.verify
60
+ end
61
+
62
+ def test_invalid_mechanism
63
+ @stream.expect(:error, nil, [Vines::SaslErrors::InvalidMechanism.new])
64
+ node = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="bogus">tokens</auth>})
65
+ assert_nothing_raised { @state.node(node) }
66
+ assert @stream.verify
67
+ end
68
+
69
+ def test_missing_text
70
+ @stream.expect(:error, nil, [Vines::SaslErrors::MalformedRequest.new])
71
+ node = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="PLAIN"></auth>})
72
+ assert_nothing_raised { @state.node(node) }
73
+ assert @stream.verify
74
+ end
75
+
76
+ def test_plain_auth_storage_error
77
+ @stream.expect(:storage, MockStorage.new(true))
78
+ @stream.expect(:error, nil, [Vines::SaslErrors::TemporaryAuthFailure.new])
79
+ node = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="PLAIN">tokens</auth>})
80
+ assert_nothing_raised { @state.node(node) }
81
+ assert @stream.verify
82
+ end
83
+
84
+ def test_plain_auth_invalid_password
85
+ @stream.expect(:storage, MockStorage.new)
86
+ @stream.expect(:error, nil, [Vines::SaslErrors::NotAuthorized.new])
87
+ node = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="PLAIN">#{Base64.encode64("alice@wonderland.lit\000\000bogus")}</auth>})
88
+ assert_nothing_raised { @state.node(node) }
89
+ assert @stream.verify
90
+ end
91
+
92
+ def test_plain_auth_valid_password
93
+ user = Vines::User.new(:jid => 'alice@wonderland.lit')
94
+ @stream.expect(:storage, MockStorage.new)
95
+ @stream.expect(:user, user)
96
+ @stream.expect(:user=, nil, [user])
97
+ @stream.expect(:write, nil, [%Q{<success xmlns="#{Vines::NAMESPACES[:sasl]}"/>}])
98
+ @stream.expect(:advance, nil, [Vines::Stream::Client::BindRestart.new(@stream)])
99
+ node = node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="PLAIN">#{Base64.encode64("alice@wonderland.lit\000\000secr3t")}</auth>})
100
+ assert_nothing_raised { @state.node(node) }
101
+ assert @stream.verify
102
+ end
103
+
104
+ def test_max_auth_attempts_policy_violation
105
+ @stream.expect(:storage, MockStorage.new)
106
+ node = proc do
107
+ node(%Q{<auth xmlns="#{Vines::NAMESPACES[:sasl]}" mechanism="PLAIN">#{Base64.encode64("alice@wonderland.lit\000\000bogus")}</auth>})
108
+ end
109
+
110
+ @stream.expect(:error, nil, [Vines::SaslErrors::NotAuthorized.new])
111
+ assert_nothing_raised { @state.node(node.call) }
112
+ assert @stream.verify
113
+
114
+ assert_nothing_raised { @state.node(node.call) }
115
+ assert @stream.verify
116
+
117
+ @stream.expect(:error, nil, [Vines::StreamErrors::PolicyViolation.new])
118
+ assert_nothing_raised { @state.node(node.call) }
119
+ assert @stream.verify
120
+ end
121
+
122
+ private
123
+
124
+ def node(xml)
125
+ Nokogiri::XML(xml).root
126
+ end
127
+ end