diaspora-vines 0.1.26 → 0.1.27
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.
- checksums.yaml +4 -4
- data/conf/config.rb +1 -0
- data/lib/vines.rb +12 -5
- data/lib/vines/config/host.rb +9 -0
- data/lib/vines/kit.rb +7 -0
- data/lib/vines/node.rb +31 -0
- data/lib/vines/router.rb +5 -0
- data/lib/vines/stanza/presence.rb +16 -1
- data/lib/vines/storage.rb +10 -1
- data/lib/vines/storage/sql.rb +18 -5
- data/lib/vines/stream.rb +12 -10
- data/lib/vines/stream/client/bind_restart.rb +6 -0
- data/lib/vines/stream/http.rb +13 -11
- data/lib/vines/stream/server.rb +46 -13
- data/lib/vines/stream/server/auth_method.rb +78 -0
- data/lib/vines/stream/server/auth_restart.rb +27 -1
- data/lib/vines/stream/server/outbound/auth.rb +43 -9
- data/lib/vines/stream/server/outbound/auth_dialback_result.rb +39 -0
- data/lib/vines/stream/server/outbound/auth_external.rb +33 -0
- data/lib/vines/stream/server/outbound/{auth_result.rb → auth_external_result.rb} +1 -1
- data/lib/vines/stream/server/outbound/auth_restart.rb +8 -1
- data/lib/vines/stream/server/outbound/authoritative.rb +48 -0
- data/lib/vines/stream/server/outbound/start.rb +1 -1
- data/lib/vines/stream/server/start.rb +29 -2
- data/lib/vines/stream/state.rb +3 -17
- data/lib/vines/version.rb +1 -1
- data/test/storage/sql_test.rb +16 -0
- data/test/store_test.rb +3 -1
- data/test/stream/server/auth_method_test.rb +101 -0
- data/test/stream/server/outbound/auth_dialback_result_test.rb +37 -0
- data/test/stream/server/outbound/auth_external_test.rb +75 -0
- data/test/stream/server/outbound/auth_restart_test.rb +53 -0
- data/test/stream/server/outbound/auth_test.rb +59 -43
- data/test/stream/server/outbound/authoritative_test.rb +66 -0
- data/test/stream/server/outbound/start_test.rb +33 -0
- data/test/stream/server/start_test.rb +70 -0
- metadata +33 -10
- data/lib/vines/stream/server/outbound/tls.rb +0 -30
- data/lib/vines/stream/server/tls.rb +0 -13
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Server
|
6
|
+
class AuthMethod < State
|
7
|
+
VERIFY, VALID_TYPE, INVALID_TYPE = %w[verify valid invalid].map {|t| t.freeze }
|
8
|
+
STARTTLS, RESULT, FROM, TO = %w[starttls result from to].map {|s| s.freeze }
|
9
|
+
PROCEED = %Q{<proceed xmlns="#{NAMESPACES[:tls]}"/>}.freeze
|
10
|
+
FAILURE = %Q{<failure xmlns="#{NAMESPACES[:tls]}"/>}.freeze
|
11
|
+
|
12
|
+
def initialize(stream, success=AuthRestart)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def node(node)
|
17
|
+
if dialback_verify?(node)
|
18
|
+
id, from, to = %w[id from to].map {|a| node[a] }
|
19
|
+
key = node.text
|
20
|
+
outbound_stream = stream.router.stream_by_id(id)
|
21
|
+
|
22
|
+
unless outbound_stream && outbound_stream.state.is_a?(Stream::Server::Outbound::AuthDialbackResult)
|
23
|
+
stream.write(%Q{<db:verify from="#{to}" to=#{from} id=#{id} type="error"><error type="cancel"><item-not-found xmlns="#{NAMESPACES[:stanzas]}" /></error></db:verify>})
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
secret = outbound_stream.state.dialback_secret
|
28
|
+
type = Kit.dialback_key(secret, from, to, id) == key ? VALID_TYPE : INVALID_TYPE
|
29
|
+
stream.write(%Q{<db:verify from="#{to}" to="#{from}" id="#{id}" type="#{type}" />})
|
30
|
+
stream.close_connection_after_writing
|
31
|
+
elsif starttls?(node)
|
32
|
+
if stream.encrypt?
|
33
|
+
stream.write(PROCEED)
|
34
|
+
stream.encrypt
|
35
|
+
stream.reset
|
36
|
+
advance
|
37
|
+
else
|
38
|
+
stream.write(FAILURE)
|
39
|
+
stream.write('</stream:stream>')
|
40
|
+
stream.close_connection_after_writing
|
41
|
+
end
|
42
|
+
elsif dialback_result?(node)
|
43
|
+
begin
|
44
|
+
Vines::Stream::Server.start(stream.config, node[FROM], node[TO], true) do |authoritative|
|
45
|
+
if authoritative
|
46
|
+
authoritative.write("<db:verify from='#{node[TO]}' id='#{stream.id}' to='#{node[FROM]}'>#{node.text}</db:verify>")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# We need to be discoverable for the dialback connection
|
50
|
+
stream.router << stream
|
51
|
+
rescue StanzaErrors::RemoteServerNotFound => e
|
52
|
+
stream.write("<db:result from='#{node[TO]}' to='#{node[FROM]}' " \
|
53
|
+
"type='error'><error type='cancel'><item-not-found " \
|
54
|
+
"xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></db:result>")
|
55
|
+
stream.close_connection_after_writing
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise StreamErrors::NotAuthorized
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def starttls?(node)
|
65
|
+
node.name == STARTTLS && namespace(node) == NAMESPACES[:tls]
|
66
|
+
end
|
67
|
+
|
68
|
+
def dialback_verify?(node)
|
69
|
+
node.name == VERIFY && namespace(node) == NAMESPACES[:legacy_dialback]
|
70
|
+
end
|
71
|
+
|
72
|
+
def dialback_result?(node)
|
73
|
+
node.name == RESULT && namespace(node) == NAMESPACES[:legacy_dialback]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -3,10 +3,36 @@
|
|
3
3
|
module Vines
|
4
4
|
class Stream
|
5
5
|
class Server
|
6
|
-
class AuthRestart <
|
6
|
+
class AuthRestart < State
|
7
7
|
def initialize(stream, success=Auth)
|
8
8
|
super
|
9
9
|
end
|
10
|
+
|
11
|
+
def node(node)
|
12
|
+
raise StreamErrors::NotAuthorized unless stream?(node)
|
13
|
+
stream.start(node)
|
14
|
+
doc = Document.new
|
15
|
+
features = doc.create_element('stream:features')
|
16
|
+
if stream.dialback_retry?
|
17
|
+
if stream.vhost.force_s2s_encryption?
|
18
|
+
stream.close_connection
|
19
|
+
return
|
20
|
+
end
|
21
|
+
@success = AuthMethod
|
22
|
+
features << doc.create_element('dialback') do |db|
|
23
|
+
db.default_namespace = NAMESPACES[:dialback]
|
24
|
+
end
|
25
|
+
else
|
26
|
+
features << doc.create_element('mechanisms') do |parent|
|
27
|
+
parent.default_namespace = NAMESPACES[:sasl]
|
28
|
+
stream.authentication_mechanisms.each do |name|
|
29
|
+
parent << doc.create_element('mechanism', name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
stream.write(features)
|
34
|
+
advance
|
35
|
+
end
|
10
36
|
end
|
11
37
|
end
|
12
38
|
end
|
@@ -5,24 +5,58 @@ module Vines
|
|
5
5
|
class Server
|
6
6
|
class Outbound
|
7
7
|
class Auth < State
|
8
|
-
|
8
|
+
REQUIRED = 'required'.freeze
|
9
|
+
FEATURES = 'features'.freeze
|
9
10
|
|
10
|
-
def initialize(stream, success=
|
11
|
+
def initialize(stream, success=AuthDialbackResult)
|
11
12
|
super
|
12
13
|
end
|
13
14
|
|
14
15
|
def node(node)
|
15
|
-
|
16
|
-
|
17
|
-
stream.
|
18
|
-
|
16
|
+
# We have to remember tls_require for
|
17
|
+
# closing or restarting the stream
|
18
|
+
stream.outbound_tls_required(tls_required?(node))
|
19
|
+
|
20
|
+
if stream.dialback_verify_key?
|
21
|
+
@success = Authoritative
|
22
|
+
stream.callback!
|
23
|
+
advance
|
24
|
+
elsif tls?(node)
|
25
|
+
@success = TLSResult
|
26
|
+
stream.write("<starttls xmlns='#{NAMESPACES[:tls]}'/>")
|
27
|
+
advance
|
28
|
+
elsif dialback?(node)
|
29
|
+
secret = Kit.auth_token
|
30
|
+
dialback_key = Kit.dialback_key(secret, stream.remote_domain, stream.domain, stream.id)
|
31
|
+
stream.write("<db:result xmlns:db='#{NAMESPACES[:legacy_dialback]}' " \
|
32
|
+
"from='#{stream.domain}' to='#{stream.remote_domain}'>#{dialback_key}</db:result>")
|
33
|
+
advance
|
34
|
+
stream.router << stream # We need to be discoverable for the dialback connection
|
35
|
+
stream.state.dialback_secret = secret
|
36
|
+
else
|
37
|
+
raise StreamErrors::NotAuthorized
|
38
|
+
end
|
19
39
|
end
|
20
40
|
|
21
41
|
private
|
22
42
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
43
|
+
def tls_required?(node)
|
44
|
+
child = node.xpath('ns:starttls', 'ns' => NAMESPACES[:tls]).children.first
|
45
|
+
child && child.name == REQUIRED
|
46
|
+
end
|
47
|
+
|
48
|
+
def dialback?(node)
|
49
|
+
dialback = node.xpath('ns:dialback', 'ns' => NAMESPACES[:dialback]).any?
|
50
|
+
features?(node) && dialback
|
51
|
+
end
|
52
|
+
|
53
|
+
def tls?(node)
|
54
|
+
tls = node.xpath('ns:starttls', 'ns' => NAMESPACES[:tls]).any?
|
55
|
+
features?(node) && tls
|
56
|
+
end
|
57
|
+
|
58
|
+
def features?(node)
|
59
|
+
node.name == FEATURES && namespace(node) == NAMESPACES[:stream]
|
26
60
|
end
|
27
61
|
end
|
28
62
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Server
|
6
|
+
class Outbound
|
7
|
+
class AuthDialbackResult < State
|
8
|
+
RESULT, VALID, INVALID, TYPE = %w[result valid invalid type].map {|s| s.freeze }
|
9
|
+
|
10
|
+
attr_accessor :dialback_secret
|
11
|
+
|
12
|
+
def initialize(stream, success=Ready)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def node(node)
|
17
|
+
raise StreamErrors::NotAuthorized unless result?(node)
|
18
|
+
|
19
|
+
case node[TYPE]
|
20
|
+
when VALID
|
21
|
+
advance
|
22
|
+
stream.notify_connected
|
23
|
+
when INVALID
|
24
|
+
stream.close_connection
|
25
|
+
else
|
26
|
+
raise StreamErrors::NotAuthorized
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def result?(node)
|
33
|
+
node.name == RESULT && namespace(node) == NAMESPACES[:legacy_dialback]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Server
|
6
|
+
class Outbound
|
7
|
+
class AuthExternal < State
|
8
|
+
def initialize(stream, success=AuthExternalResult)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def node(node)
|
13
|
+
raise StreamErrors::NotAuthorized unless external?(node)
|
14
|
+
authzid = Base64.strict_encode64(stream.domain)
|
15
|
+
stream.write(%Q{<auth xmlns="#{NAMESPACES[:sasl]}" mechanism="EXTERNAL">#{authzid}</auth>})
|
16
|
+
advance
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def external?(node)
|
22
|
+
external = node.xpath("ns:mechanisms/ns:mechanism[text()='EXTERNAL']", 'ns' => NAMESPACES[:sasl]).any?
|
23
|
+
features?(node) && external
|
24
|
+
end
|
25
|
+
|
26
|
+
def features?(node)
|
27
|
+
node.name == 'features' && namespace(node) == NAMESPACES[:stream]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -5,12 +5,19 @@ module Vines
|
|
5
5
|
class Server
|
6
6
|
class Outbound
|
7
7
|
class AuthRestart < State
|
8
|
-
def initialize(stream, success=
|
8
|
+
def initialize(stream, success=AuthExternal)
|
9
9
|
super
|
10
10
|
end
|
11
11
|
|
12
12
|
def node(node)
|
13
13
|
raise StreamErrors::NotAuthorized unless stream?(node)
|
14
|
+
if stream.dialback_retry?
|
15
|
+
if stream.outbound_tls_required?
|
16
|
+
stream.close_connection
|
17
|
+
return
|
18
|
+
end
|
19
|
+
@success = Auth
|
20
|
+
end
|
14
21
|
advance
|
15
22
|
end
|
16
23
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Server
|
6
|
+
class Outbound
|
7
|
+
class Authoritative < State
|
8
|
+
VALID, INVALID, ERROR, TYPE = %w[valid invalid error type]
|
9
|
+
VERIFY, ID, FROM, TO = %w[verify id from to].map {|s| s.freeze }
|
10
|
+
|
11
|
+
def initialize(stream, success=nil)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def node(node)
|
16
|
+
raise StreamErrors::NotAuthorized unless authoritative?(node)
|
17
|
+
|
18
|
+
case node[TYPE]
|
19
|
+
when VALID
|
20
|
+
@inbound.write("<db:result xmlns:db='#{NAMESPACES[:legacy_dialback]}' " \
|
21
|
+
"from='#{node[TO]}' to='#{node[FROM]}' type='#{node[TYPE]}'/>")
|
22
|
+
@inbound.advance(Server::Ready.new(@inbound))
|
23
|
+
@inbound.notify_connected
|
24
|
+
when INVALID
|
25
|
+
@inbound.write("<db:result xmlns:db='#{NAMESPACES[:legacy_dialback]}' " \
|
26
|
+
"from='#{node[TO]}' to='#{node[FROM]}' type='#{node[TYPE]}'/>")
|
27
|
+
@inbound.close_connection_after_writing
|
28
|
+
else
|
29
|
+
@inbound.write("<db:result xmlns:db='#{NAMESPACES[:legacy_dialback]}' " \
|
30
|
+
"from='#{node[TO]}' to='#{node[FROM]}' type='#{ERROR}'>" \
|
31
|
+
"<error type='cancel'><item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" \
|
32
|
+
"</error></db:result>")
|
33
|
+
@inbound.close_connection_after_writing
|
34
|
+
end
|
35
|
+
stream.close_connection
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def authoritative?(node)
|
41
|
+
@inbound = stream.router.stream_by_id(node[ID])
|
42
|
+
node.name == VERIFY && namespace(node) == NAMESPACES[:legacy_dialback] && !@inbound.nil?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -3,10 +3,37 @@
|
|
3
3
|
module Vines
|
4
4
|
class Stream
|
5
5
|
class Server
|
6
|
-
class Start <
|
7
|
-
|
6
|
+
class Start < State
|
7
|
+
FROM = "from".freeze
|
8
|
+
|
9
|
+
def initialize(stream, success=AuthMethod)
|
8
10
|
super
|
9
11
|
end
|
12
|
+
|
13
|
+
def node(node)
|
14
|
+
raise StreamErrors::NotAuthorized unless stream?(node)
|
15
|
+
stream.start(node)
|
16
|
+
doc = Document.new
|
17
|
+
features = doc.create_element('stream:features', 'xmlns:stream' => NAMESPACES[:stream]) do |el|
|
18
|
+
unless stream.dialback_retry?
|
19
|
+
el << doc.create_element('starttls') do |tls|
|
20
|
+
tls.default_namespace = NAMESPACES[:tls]
|
21
|
+
tls << doc.create_element('required') if force_s2s_encryption?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
el << doc.create_element('dialback') do |db|
|
25
|
+
db.default_namespace = NAMESPACES[:dialback]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
stream.write(features)
|
29
|
+
advance
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def force_s2s_encryption?
|
35
|
+
stream.vhost.force_s2s_encryption?
|
36
|
+
end
|
10
37
|
end
|
11
38
|
end
|
12
39
|
end
|
data/lib/vines/stream/state.rb
CHANGED
@@ -8,12 +8,10 @@ module Vines
|
|
8
8
|
class State
|
9
9
|
include Nokogiri::XML
|
10
10
|
include Vines::Log
|
11
|
+
include Vines::Node
|
11
12
|
|
12
13
|
attr_accessor :stream
|
13
14
|
|
14
|
-
BODY = 'body'.freeze
|
15
|
-
STREAM = 'stream'.freeze
|
16
|
-
|
17
15
|
def initialize(stream, success=nil)
|
18
16
|
@stream, @success = stream, success
|
19
17
|
end
|
@@ -40,21 +38,9 @@ module Vines
|
|
40
38
|
stream.advance(@success.new(stream))
|
41
39
|
end
|
42
40
|
|
43
|
-
def stream?(node)
|
44
|
-
node.name == STREAM && namespace(node) == NAMESPACES[:stream]
|
45
|
-
end
|
46
|
-
|
47
|
-
def body?(node)
|
48
|
-
node.name == BODY && namespace(node) == NAMESPACES[:http_bind]
|
49
|
-
end
|
50
|
-
|
51
|
-
def namespace(node)
|
52
|
-
node.namespace ? node.namespace.href : nil
|
53
|
-
end
|
54
|
-
|
55
41
|
def to_stanza(node)
|
56
|
-
|
42
|
+
super(node, stream)
|
57
43
|
end
|
58
44
|
end
|
59
45
|
end
|
60
|
-
end
|
46
|
+
end
|
data/lib/vines/version.rb
CHANGED
data/test/storage/sql_test.rb
CHANGED
@@ -110,6 +110,18 @@ describe Vines::Storage::Sql do
|
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
|
+
def test_find_avatar_by_jid
|
114
|
+
fibered do
|
115
|
+
db = storage
|
116
|
+
|
117
|
+
assert_nil db.find_avatar_by_jid("")
|
118
|
+
assert_nil db.find_avatar_by_jid("someone@inthe.void")
|
119
|
+
|
120
|
+
image_path = db.find_avatar_by_jid(@test_user[:jid])
|
121
|
+
assert_equal @test_user[:image_url], image_path
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
113
125
|
def test_find_messages
|
114
126
|
fibered do
|
115
127
|
db = storage
|
@@ -185,6 +197,10 @@ describe Vines::Storage::Sql do
|
|
185
197
|
assert (user != nil), "no user found"
|
186
198
|
assert_equal @test_user[:name], user.name
|
187
199
|
|
200
|
+
user.roster do |contact|
|
201
|
+
assert_equal "Harry Hirsch", contact.name
|
202
|
+
end
|
203
|
+
|
188
204
|
user = db.find_user(Vines::JID.new(@test_user[:jid]))
|
189
205
|
assert (user != nil), "no user found"
|
190
206
|
assert_equal @test_user[:name], user.name
|