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,157 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Http < Client
6
+ attr_accessor :session
7
+
8
+ def initialize(config)
9
+ super
10
+ @session = Http::Session.new(self)
11
+ end
12
+
13
+ # Override +Stream#create_parser+ to provide an HTTP parser rather than
14
+ # a Nokogiri XML parser.
15
+ def create_parser
16
+ @parser = ::Http::Parser.new.tap do |p|
17
+ body = ''
18
+ p.on_body = proc {|data| body << data }
19
+ p.on_message_complete = proc {
20
+ process_request(Request.new(self, @parser, body))
21
+ body = ''
22
+ }
23
+ end
24
+ end
25
+
26
+ # If the session ID is valid, switch this stream's session to the new
27
+ # ID and return true. Some clients, like Google Chrome, reuse one stream
28
+ # for multiple sessions.
29
+ def valid_session?(sid)
30
+ if session = Sessions[sid]
31
+ @session = session
32
+ end
33
+ !!session
34
+ end
35
+
36
+ %w[max_stanza_size max_resources_per_account bind root].each do |name|
37
+ define_method name do |*args|
38
+ config[:http].send(name, *args)
39
+ end
40
+ end
41
+
42
+ def process_request(request)
43
+ if request.path == self.bind && request.options?
44
+ request.reply_to_options
45
+ elsif request.path == self.bind
46
+ body = Nokogiri::XML(request.body).root
47
+ if session = Sessions[body['sid']]
48
+ @session = session
49
+ else
50
+ @session = Http::Session.new(self)
51
+ end
52
+ @session.request(request)
53
+ @nodes.push(body)
54
+ else
55
+ request.reply_with_file(self.root)
56
+ end
57
+ end
58
+
59
+ # Alias the Stream#write method before overriding it so we can call
60
+ # it later from a Session instance.
61
+ alias :stream_write :write
62
+
63
+ # Override Stream#write to queue stanzas rather than immediately writing
64
+ # to the stream. Stanza responses must be paired with a queued request.
65
+ def write(data)
66
+ @session.write(data)
67
+ end
68
+
69
+ # Return an array of Node objects inside the body element.
70
+ # TODO This parses the XML again just to strip namespaces. Figure out
71
+ # Nokogiri namespace handling instead.
72
+ def parse_body(body)
73
+ body.namespace = nil
74
+ body.elements.map do |node|
75
+ Nokogiri::XML(node.to_s.sub(' xmlns="jabber:client"', '')).root
76
+ end
77
+ end
78
+
79
+ def start(node)
80
+ domain, type, hold, wait, rid = %w[to content hold wait rid].map {|a| (node[a] || '').strip }
81
+ version = node.attribute_with_ns('version', NAMESPACES[:bosh]).value rescue nil
82
+
83
+ @session.inactivity = 20
84
+ @session.domain = domain
85
+ @session.content_type = type unless type.empty?
86
+ @session.hold = hold.to_i unless hold.empty?
87
+ @session.wait = wait.to_i unless wait.empty?
88
+
89
+ raise StreamErrors::UndefinedCondition.new('rid required') if rid.empty?
90
+ raise StreamErrors::UnsupportedVersion unless version == '1.0'
91
+ raise StreamErrors::ImproperAddressing unless valid_address?(domain)
92
+ raise StreamErrors::HostUnknown unless config.vhost?(domain)
93
+ raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:http_bind]
94
+
95
+ Sessions[@session.id] = @session
96
+ send_stream_header
97
+ end
98
+
99
+ def terminate
100
+ doc = Nokogiri::XML::Document.new
101
+ node = doc.create_element('body',
102
+ 'type' => 'terminate',
103
+ 'xmlns' => NAMESPACES[:http_bind])
104
+ @session.reply(node)
105
+ close_stream
106
+ end
107
+
108
+ private
109
+
110
+ def send_stream_header
111
+ doc = Nokogiri::XML::Document.new
112
+ node = doc.create_element('body',
113
+ 'charsets' => 'UTF-8',
114
+ 'from' => @session.domain,
115
+ 'hold' => @session.hold,
116
+ 'inactivity' => @session.inactivity,
117
+ 'polling' => '5',
118
+ 'requests' => '2',
119
+ 'sid' => @session.id,
120
+ 'ver' => '1.6',
121
+ 'wait' => @session.wait,
122
+ 'xmpp:version' => '1.0',
123
+ 'xmlns' => NAMESPACES[:http_bind],
124
+ 'xmlns:xmpp' => NAMESPACES[:bosh],
125
+ 'xmlns:stream' => NAMESPACES[:stream])
126
+
127
+ node << doc.create_element('stream:features') do |el|
128
+ el << doc.create_element('mechanisms') do |mechanisms|
129
+ mechanisms.default_namespace = NAMESPACES[:sasl]
130
+ mechanisms << doc.create_element('mechanism', 'PLAIN')
131
+ end
132
+ end
133
+ @session.reply(node)
134
+ end
135
+
136
+ # Override +Stream#send_stream_error+ to wrap the error XML in a BOSH
137
+ # terminate body tag.
138
+ def send_stream_error(e)
139
+ doc = Nokogiri::XML::Document.new
140
+ node = doc.create_element('body',
141
+ 'condition' => 'remote-stream-error',
142
+ 'type' => 'terminate',
143
+ 'xmlns' => NAMESPACES[:http_bind],
144
+ 'xmlns:stream' => NAMESPACES[:stream])
145
+ node.inner_html = e.to_xml
146
+ @session.reply(node)
147
+ end
148
+
149
+ # Override +Stream#close_stream+ to simply close the connection without
150
+ # writing a closing stream tag.
151
+ def close_stream
152
+ close_connection_after_writing
153
+ @session.close
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,79 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Parser < Nokogiri::XML::SAX::Document
6
+ include Nokogiri::XML
7
+ STREAM_NAME = 'stream'.freeze
8
+ STREAM_URI = 'http://etherx.jabber.org/streams'.freeze
9
+ IGNORE = NAMESPACES.values_at(:client, :component, :server)
10
+
11
+ def initialize(&block)
12
+ @listeners, @node = Hash.new {|h, k| h[k] = []}, nil
13
+ @parser = Nokogiri::XML::SAX::PushParser.new(self)
14
+ instance_eval(&block) if block
15
+ end
16
+
17
+ [:stream_open, :stream_close, :stanza].each do |name|
18
+ define_method(name) do |&block|
19
+ @listeners[name] << block
20
+ end
21
+ end
22
+
23
+ def <<(data)
24
+ @parser << data
25
+ self
26
+ end
27
+
28
+ def start_element_namespace(name, attrs=[], prefix=nil, uri=nil, ns=[])
29
+ el = node(name, attrs, prefix, uri, ns)
30
+ if stream?(name, uri)
31
+ notify(:stream_open, el)
32
+ else
33
+ @node << el if @node
34
+ @node = el
35
+ end
36
+ end
37
+
38
+ def end_element_namespace(name, prefix=nil, uri=nil)
39
+ if stream?(name, uri)
40
+ notify(:stream_close)
41
+ elsif @node.parent != @node.document
42
+ @node = @node.parent
43
+ else
44
+ notify(:stanza, @node)
45
+ @node = nil
46
+ end
47
+ end
48
+
49
+ def characters(chars)
50
+ @node << Text.new(chars, @node.document) if @node
51
+ end
52
+ alias :cdata_block :characters
53
+
54
+ private
55
+
56
+ def notify(msg, node=nil)
57
+ @listeners[msg].each do |b|
58
+ (node ? b.call(node) : b.call) rescue nil
59
+ end
60
+ end
61
+
62
+ def stream?(name, uri)
63
+ name == STREAM_NAME && uri == STREAM_URI
64
+ end
65
+
66
+ def node(name, attrs=[], prefix=nil, uri=nil, ns=[])
67
+ ignore = stream?(name, uri) ? [] : IGNORE
68
+ doc = @node ? @node.document : Document.new
69
+ node = doc.create_element(name) do |node|
70
+ attrs.each {|attr| node[attr.localname] = attr.value }
71
+ ns.each {|prefix, uri| node.add_namespace(prefix, uri) unless ignore.include?(uri) }
72
+ doc << node unless @node
73
+ end
74
+ node.namespace = node.add_namespace(prefix, uri) unless ignore.include?(uri)
75
+ node
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,128 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ # Provides plain (username/password) and external (TLS certificate) SASL
6
+ # authentication to client and server streams.
7
+ class SASL
8
+ include Vines::Log
9
+ EMPTY = '='.freeze
10
+
11
+ def initialize(stream)
12
+ @stream = stream
13
+ end
14
+
15
+ # Authenticate server-to-server streams, comparing their domain to their
16
+ # SSL certificate.
17
+ #
18
+ # http://xmpp.org/extensions/xep-0178.html#s2s
19
+ #
20
+ # encoded - The Base64 encoded remote domain name String sent by the
21
+ # server stream.
22
+ #
23
+ # Returns true if the Base64 encoded domain matches the TLS certificate
24
+ # presented earlier in stream negotiation.
25
+ #
26
+ # Raises a SaslError if authentication failed.
27
+ def external_auth(encoded)
28
+ unless encoded == EMPTY
29
+ authzid = decode64(encoded)
30
+ matches_from = (authzid == @stream.remote_domain)
31
+ raise SaslErrors::InvalidAuthzid unless matches_from
32
+ end
33
+ matches_from = @stream.cert_domain_matches?(@stream.remote_domain)
34
+ matches_from or raise SaslErrors::NotAuthorized
35
+ end
36
+
37
+ # Authenticate client-to-server streams using a username and password.
38
+ #
39
+ # encoded - The Base64 encoded jid and password String sent by the
40
+ # client stream.
41
+ #
42
+ # Returns the authenticated User or raises SaslError if authentication failed.
43
+ def plain_auth(encoded)
44
+ jid, password = decode_credentials(encoded)
45
+ user = authenticate(jid, password)
46
+ user or raise SaslErrors::NotAuthorized
47
+ end
48
+
49
+ private
50
+
51
+ # Storage backends should not raise errors, but if an unexpected error
52
+ # occurs during authentication, convert it to a temporary-auth-failure.
53
+ #
54
+ # jid - The user's jid String.
55
+ # password - The String password.
56
+ #
57
+ # Returns the authenticated User or nil if authentication failed.
58
+ #
59
+ # Raises TemoraryAuthFailure if the storage system failed.
60
+ def authenticate(jid, password)
61
+ log.info("Authenticating user: %s" % jid)
62
+ @stream.storage.authenticate(jid, password).tap do |user|
63
+ log.info("Authentication succeeded: %s" % user.jid) if user
64
+ end
65
+ rescue => e
66
+ log.error("Failed to authenticate: #{e.to_s}")
67
+ raise SaslErrors::TemporaryAuthFailure
68
+ end
69
+
70
+ # Return the JID and password decoded from the Base64 encoded SASL PLAIN
71
+ # credentials formatted as authzid\0authcid\0password.
72
+ #
73
+ # http://tools.ietf.org/html/rfc6120#section-6.3.8
74
+ # http://tools.ietf.org/html/rfc4616
75
+ #
76
+ # encoded - The Base64 encoded String from which to extract jid and password.
77
+ #
78
+ # Returns an Array of jid String and password String.
79
+ def decode_credentials(encoded)
80
+ authzid, node, password = decode64(encoded).split("\x00")
81
+ raise SaslErrors::NotAuthorized if node.nil? || node.empty? || password.nil? || password.empty?
82
+ jid = JID.new(node, @stream.domain) rescue (raise SaslErrors::NotAuthorized)
83
+ validate_authzid!(authzid, jid)
84
+ [jid, password]
85
+ end
86
+
87
+ # An optional SASL authzid allows a user to authenticate with one
88
+ # user name and password and then have their connection authorized as a
89
+ # different ID (the authzid). We don't support that, so raise an error if
90
+ # the authzid is provided and different than the authcid.
91
+ #
92
+ # Most clients don't send an authzid at all because it's optional and not
93
+ # widely supported. However, Strophe and Blather send a bare JID, in
94
+ # compliance with RFC 6120, but Smack sends just the user name as the
95
+ # authzid. So, take care to handle non-compliant clients here.
96
+ #
97
+ # http://tools.ietf.org/html/rfc6120#section-6.3.8
98
+ #
99
+ # authzid - The authzid String (may be nil).
100
+ # jid - The username String.
101
+ #
102
+ # Returns nothing.
103
+ def validate_authzid!(authzid, jid)
104
+ return if authzid.nil? || authzid.empty?
105
+ authzid.downcase!
106
+ smack = authzid == jid.node
107
+ compliant = authzid == jid.to_s
108
+ raise SaslErrors::InvalidAuthzid unless compliant || smack
109
+ end
110
+
111
+ # Decode the Base64 encoded string, raising an error for invalid data.
112
+ #
113
+ # http://tools.ietf.org/html/rfc6120#section-13.9.1
114
+ #
115
+ # encoded - The Base64 encoded String.
116
+ #
117
+ # Returns a UTF-8 String.
118
+ def decode64(encoded)
119
+ Base64.strict_decode64(encoded).tap do |decoded|
120
+ decoded.force_encoding(Encoding::UTF_8)
121
+ raise SaslErrors::IncorrectEncoding unless decoded.valid_encoding?
122
+ end
123
+ rescue
124
+ raise SaslErrors::IncorrectEncoding
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Auth < Client::Auth
7
+ def initialize(stream, success=FinalRestart)
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class AuthRestart < Client::AuthRestart
7
+ def initialize(stream, success=Auth)
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class FinalRestart < State
7
+ def initialize(stream, success=Ready)
8
+ super
9
+ end
10
+
11
+ def node(node)
12
+ raise StreamErrors::NotAuthorized unless stream?(node)
13
+ stream.start(node)
14
+ stream.write('<stream:features/>')
15
+ stream.router << stream
16
+ advance
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Outbound
7
+ class Auth < State
8
+ NS = NAMESPACES[:sasl]
9
+
10
+ def initialize(stream, success=AuthResult)
11
+ super
12
+ end
13
+
14
+ 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
19
+ end
20
+
21
+ private
22
+
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
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Outbound
7
+ class AuthRestart < State
8
+ def initialize(stream, success=Auth)
9
+ super
10
+ end
11
+
12
+ def node(node)
13
+ raise StreamErrors::NotAuthorized unless stream?(node)
14
+ advance
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Outbound
7
+ class AuthResult < State
8
+ SUCCESS = 'success'.freeze
9
+ FAILURE = 'failure'.freeze
10
+
11
+ def initialize(stream, success=FinalRestart)
12
+ super
13
+ end
14
+
15
+ def node(node)
16
+ raise StreamErrors::NotAuthorized unless namespace(node) == NAMESPACES[:sasl]
17
+ case node.name
18
+ when SUCCESS
19
+ stream.start(node)
20
+ stream.reset
21
+ advance
22
+ when FAILURE
23
+ stream.close_connection
24
+ else
25
+ raise StreamErrors::NotAuthorized
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Outbound
7
+ class FinalFeatures < State
8
+ def initialize(stream, success=Server::Ready)
9
+ super
10
+ end
11
+
12
+ def node(node)
13
+ raise StreamErrors::NotAuthorized unless empty_features?(node)
14
+ stream.router << stream
15
+ advance
16
+ stream.notify_connected
17
+ end
18
+
19
+ private
20
+
21
+ def empty_features?(node)
22
+ node.name == 'features' && namespace(node) == NAMESPACES[:stream] && node.elements.empty?
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Outbound
7
+ class FinalRestart < State
8
+ def initialize(stream, success=FinalFeatures)
9
+ super
10
+ end
11
+
12
+ def node(node)
13
+ raise StreamErrors::NotAuthorized unless stream?(node)
14
+ advance
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Outbound
7
+ class Start < State
8
+ def initialize(stream, success=TLS)
9
+ super
10
+ end
11
+
12
+ def node(node)
13
+ raise StreamErrors::NotAuthorized unless stream?(node)
14
+ advance
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Outbound
7
+ class TLS < State
8
+ NS = NAMESPACES[:tls]
9
+
10
+ def initialize(stream, success=TLSResult)
11
+ super
12
+ end
13
+
14
+ def node(node)
15
+ raise StreamErrors::NotAuthorized unless tls?(node)
16
+ stream.write("<starttls xmlns='#{NS}'/>")
17
+ advance
18
+ end
19
+
20
+ private
21
+
22
+ def tls?(node)
23
+ tls = node.xpath('ns:starttls', 'ns' => NS).any?
24
+ node.name == 'features' && namespace(node) == NAMESPACES[:stream] && tls
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Outbound
7
+ class TLSResult < State
8
+ NS = NAMESPACES[:tls]
9
+ PROCEED = 'proceed'.freeze
10
+ FAILURE = 'failure'.freeze
11
+
12
+ def initialize(stream, success=AuthRestart)
13
+ super
14
+ end
15
+
16
+ def node(node)
17
+ raise StreamErrors::NotAuthorized unless namespace(node) == NS
18
+ case node.name
19
+ when PROCEED
20
+ stream.encrypt
21
+ stream.start(node)
22
+ stream.reset
23
+ advance
24
+ when FAILURE
25
+ stream.close_connection
26
+ else
27
+ raise StreamErrors::NotAuthorized
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Ready < State
7
+ def node(node)
8
+ stanza = to_stanza(node)
9
+ raise StreamErrors::UnsupportedStanzaType unless stanza
10
+ to, from = stanza.validate_to, stanza.validate_from
11
+ raise StreamErrors::ImproperAddressing unless to && from
12
+ raise StreamErrors::InvalidFrom unless from.domain == stream.remote_domain
13
+ raise StreamErrors::HostUnknown unless to.domain == stream.domain
14
+ stream.user = User.new(jid: from)
15
+ if stanza.local? || stanza.to_pubsub_domain?
16
+ stanza.process
17
+ else
18
+ stanza.route
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class Start < Client::Start
7
+ def initialize(stream, success=TLS)
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stream
5
+ class Server
6
+ class TLS < Client::TLS
7
+ def initialize(stream, success=AuthRestart)
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end