vines 0.1.0
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.
- data/LICENSE +19 -0
- data/README +34 -0
- data/Rakefile +55 -0
- data/bin/vines +95 -0
- data/conf/certs/README +32 -0
- data/conf/certs/ca-bundle.crt +3987 -0
- data/conf/config.rb +114 -0
- data/lib/vines.rb +155 -0
- data/lib/vines/command/bcrypt.rb +12 -0
- data/lib/vines/command/cert.rb +49 -0
- data/lib/vines/command/init.rb +58 -0
- data/lib/vines/command/ldap.rb +35 -0
- data/lib/vines/command/restart.rb +12 -0
- data/lib/vines/command/schema.rb +24 -0
- data/lib/vines/command/start.rb +28 -0
- data/lib/vines/command/stop.rb +18 -0
- data/lib/vines/config.rb +191 -0
- data/lib/vines/contact.rb +99 -0
- data/lib/vines/daemon.rb +78 -0
- data/lib/vines/error.rb +150 -0
- data/lib/vines/jid.rb +56 -0
- data/lib/vines/kit.rb +23 -0
- data/lib/vines/router.rb +125 -0
- data/lib/vines/stanza.rb +55 -0
- data/lib/vines/stanza/iq.rb +50 -0
- data/lib/vines/stanza/iq/auth.rb +18 -0
- data/lib/vines/stanza/iq/disco_info.rb +25 -0
- data/lib/vines/stanza/iq/disco_items.rb +23 -0
- data/lib/vines/stanza/iq/error.rb +16 -0
- data/lib/vines/stanza/iq/ping.rb +16 -0
- data/lib/vines/stanza/iq/query.rb +10 -0
- data/lib/vines/stanza/iq/result.rb +16 -0
- data/lib/vines/stanza/iq/roster.rb +153 -0
- data/lib/vines/stanza/iq/session.rb +22 -0
- data/lib/vines/stanza/iq/vcard.rb +58 -0
- data/lib/vines/stanza/message.rb +41 -0
- data/lib/vines/stanza/presence.rb +119 -0
- data/lib/vines/stanza/presence/error.rb +23 -0
- data/lib/vines/stanza/presence/probe.rb +38 -0
- data/lib/vines/stanza/presence/subscribe.rb +66 -0
- data/lib/vines/stanza/presence/subscribed.rb +64 -0
- data/lib/vines/stanza/presence/unavailable.rb +15 -0
- data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
- data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
- data/lib/vines/storage.rb +216 -0
- data/lib/vines/storage/couchdb.rb +119 -0
- data/lib/vines/storage/ldap.rb +59 -0
- data/lib/vines/storage/local.rb +66 -0
- data/lib/vines/storage/redis.rb +108 -0
- data/lib/vines/storage/sql.rb +174 -0
- data/lib/vines/store.rb +51 -0
- data/lib/vines/stream.rb +198 -0
- data/lib/vines/stream/client.rb +131 -0
- data/lib/vines/stream/client/auth.rb +94 -0
- data/lib/vines/stream/client/auth_restart.rb +33 -0
- data/lib/vines/stream/client/bind.rb +58 -0
- data/lib/vines/stream/client/bind_restart.rb +25 -0
- data/lib/vines/stream/client/closed.rb +13 -0
- data/lib/vines/stream/client/ready.rb +15 -0
- data/lib/vines/stream/client/start.rb +27 -0
- data/lib/vines/stream/client/tls.rb +37 -0
- data/lib/vines/stream/component.rb +53 -0
- data/lib/vines/stream/component/handshake.rb +25 -0
- data/lib/vines/stream/component/ready.rb +24 -0
- data/lib/vines/stream/component/start.rb +19 -0
- data/lib/vines/stream/http.rb +111 -0
- data/lib/vines/stream/http/http_request.rb +22 -0
- data/lib/vines/stream/http/http_state.rb +139 -0
- data/lib/vines/stream/http/http_states.rb +53 -0
- data/lib/vines/stream/parser.rb +78 -0
- data/lib/vines/stream/server.rb +126 -0
- data/lib/vines/stream/server/auth.rb +13 -0
- data/lib/vines/stream/server/auth_restart.rb +19 -0
- data/lib/vines/stream/server/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth.rb +31 -0
- data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
- data/lib/vines/stream/server/outbound/final_features.rb +27 -0
- data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/start.rb +20 -0
- data/lib/vines/stream/server/outbound/tls.rb +30 -0
- data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
- data/lib/vines/stream/server/ready.rb +20 -0
- data/lib/vines/stream/server/start.rb +13 -0
- data/lib/vines/stream/server/tls.rb +13 -0
- data/lib/vines/stream/state.rb +55 -0
- data/lib/vines/token_bucket.rb +46 -0
- data/lib/vines/user.rb +124 -0
- data/lib/vines/version.rb +5 -0
- data/lib/vines/xmpp_server.rb +25 -0
- data/test/config_test.rb +396 -0
- data/test/error_test.rb +59 -0
- data/test/ext/nokogiri.rb +14 -0
- data/test/jid_test.rb +71 -0
- data/test/kit_test.rb +21 -0
- data/test/router_test.rb +60 -0
- data/test/stanza/iq/roster_test.rb +198 -0
- data/test/stanza/iq/session_test.rb +30 -0
- data/test/stanza/iq/vcard_test.rb +159 -0
- data/test/stanza/message_test.rb +124 -0
- data/test/stanza/presence/subscribe_test.rb +75 -0
- data/test/storage/couchdb_test.rb +102 -0
- data/test/storage/ldap_test.rb +207 -0
- data/test/storage/local_test.rb +54 -0
- data/test/storage/redis_test.rb +75 -0
- data/test/storage/sql_test.rb +55 -0
- data/test/storage/storage_tests.rb +134 -0
- data/test/storage_test.rb +90 -0
- data/test/stream/client/auth_test.rb +127 -0
- data/test/stream/client/ready_test.rb +47 -0
- data/test/stream/component/handshake_test.rb +46 -0
- data/test/stream/component/ready_test.rb +105 -0
- data/test/stream/component/start_test.rb +41 -0
- data/test/stream/parser_test.rb +121 -0
- data/test/stream/server/outbound/auth_test.rb +77 -0
- data/test/stream/server/ready_test.rb +100 -0
- data/test/token_bucket_test.rb +24 -0
- data/test/user_test.rb +64 -0
- 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
|