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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/conf/config.rb +1 -0
  3. data/lib/vines.rb +12 -5
  4. data/lib/vines/config/host.rb +9 -0
  5. data/lib/vines/kit.rb +7 -0
  6. data/lib/vines/node.rb +31 -0
  7. data/lib/vines/router.rb +5 -0
  8. data/lib/vines/stanza/presence.rb +16 -1
  9. data/lib/vines/storage.rb +10 -1
  10. data/lib/vines/storage/sql.rb +18 -5
  11. data/lib/vines/stream.rb +12 -10
  12. data/lib/vines/stream/client/bind_restart.rb +6 -0
  13. data/lib/vines/stream/http.rb +13 -11
  14. data/lib/vines/stream/server.rb +46 -13
  15. data/lib/vines/stream/server/auth_method.rb +78 -0
  16. data/lib/vines/stream/server/auth_restart.rb +27 -1
  17. data/lib/vines/stream/server/outbound/auth.rb +43 -9
  18. data/lib/vines/stream/server/outbound/auth_dialback_result.rb +39 -0
  19. data/lib/vines/stream/server/outbound/auth_external.rb +33 -0
  20. data/lib/vines/stream/server/outbound/{auth_result.rb → auth_external_result.rb} +1 -1
  21. data/lib/vines/stream/server/outbound/auth_restart.rb +8 -1
  22. data/lib/vines/stream/server/outbound/authoritative.rb +48 -0
  23. data/lib/vines/stream/server/outbound/start.rb +1 -1
  24. data/lib/vines/stream/server/start.rb +29 -2
  25. data/lib/vines/stream/state.rb +3 -17
  26. data/lib/vines/version.rb +1 -1
  27. data/test/storage/sql_test.rb +16 -0
  28. data/test/store_test.rb +3 -1
  29. data/test/stream/server/auth_method_test.rb +101 -0
  30. data/test/stream/server/outbound/auth_dialback_result_test.rb +37 -0
  31. data/test/stream/server/outbound/auth_external_test.rb +75 -0
  32. data/test/stream/server/outbound/auth_restart_test.rb +53 -0
  33. data/test/stream/server/outbound/auth_test.rb +59 -43
  34. data/test/stream/server/outbound/authoritative_test.rb +66 -0
  35. data/test/stream/server/outbound/start_test.rb +33 -0
  36. data/test/stream/server/start_test.rb +70 -0
  37. metadata +33 -10
  38. data/lib/vines/stream/server/outbound/tls.rb +0 -30
  39. 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 < Client::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
- NS = NAMESPACES[:sasl]
8
+ REQUIRED = 'required'.freeze
9
+ FEATURES = 'features'.freeze
9
10
 
10
- def initialize(stream, success=AuthResult)
11
+ def initialize(stream, success=AuthDialbackResult)
11
12
  super
12
13
  end
13
14
 
14
15
  def node(node)
15
- raise StreamErrors::NotAuthorized unless external?(node)
16
- authzid = Base64.strict_encode64(stream.domain)
17
- stream.write(%Q{<auth xmlns="#{NS}" mechanism="EXTERNAL">#{authzid}</auth>})
18
- advance
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 external?(node)
24
- external = node.xpath("ns:mechanisms/ns:mechanism[text()='EXTERNAL']", 'ns' => NS).any?
25
- node.name == 'features' && namespace(node) == NAMESPACES[:stream] && external
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
@@ -4,7 +4,7 @@ module Vines
4
4
  class Stream
5
5
  class Server
6
6
  class Outbound
7
- class AuthResult < State
7
+ class AuthExternalResult < State
8
8
  SUCCESS = 'success'.freeze
9
9
  FAILURE = 'failure'.freeze
10
10
 
@@ -5,12 +5,19 @@ module Vines
5
5
  class Server
6
6
  class Outbound
7
7
  class AuthRestart < State
8
- def initialize(stream, success=Auth)
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
@@ -5,7 +5,7 @@ module Vines
5
5
  class Server
6
6
  class Outbound
7
7
  class Start < State
8
- def initialize(stream, success=TLS)
8
+ def initialize(stream, success=Auth)
9
9
  super
10
10
  end
11
11
 
@@ -3,10 +3,37 @@
3
3
  module Vines
4
4
  class Stream
5
5
  class Server
6
- class Start < Client::Start
7
- def initialize(stream, success=TLS)
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
@@ -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
- Stanza.from_node(node, stream)
42
+ super(node, stream)
57
43
  end
58
44
  end
59
45
  end
60
- end
46
+ end
data/lib/vines/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Vines
4
4
  # vines forked version 0.4.10
5
- VERSION = '0.1.26'
5
+ VERSION = '0.1.27'
6
6
  end
@@ -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