lygneo-vines 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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/daemon.rb +78 -0
  30. data/lib/vines/error.rb +150 -0
  31. data/lib/vines/follower.rb +111 -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/error_test.rb +58 -0
  125. data/test/ext/nokogiri.rb +14 -0
  126. data/test/follower_test.rb +102 -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