Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(483)

Side by Side Diff: remoting/webapp/crd/js/xmpp_login_handler.js

Issue 1133913002: [Chromoting] Move shared webapp JS files from crd/js -> base/js (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
9
10 /**
11 * XmppLoginHandler handles authentication handshake for XmppConnection. It
12 * receives incoming data using onDataReceived(), calls |sendMessageCallback|
13 * to send outgoing messages and calls |onHandshakeDoneCallback| after
14 * authentication is finished successfully or |onErrorCallback| on error.
15 *
16 * See RFC3920 for description of XMPP and authentication handshake.
17 *
18 * @param {string} server Domain name of the server we are connecting to.
19 * @param {string} username Username.
20 * @param {string} authToken OAuth2 token.
21 * @param {boolean} needHandshakeBeforeTls Set to true when <starttls> handshake
22 * is required before starting TLS. Otherwise TLS can be started right away.
23 * @param {function(string):void} sendMessageCallback Callback to call to send
24 * a message.
25 * @param {function():void} startTlsCallback Callback to call to start TLS on
26 * the underlying socket.
27 * @param {function(string, remoting.XmppStreamParser):void}
28 * onHandshakeDoneCallback Callback to call after authentication is
29 * completed successfully
30 * @param {function(!remoting.Error, string):void} onErrorCallback Callback to
31 * call on error. Can be called at any point during lifetime of connection.
32 * @constructor
33 */
34 remoting.XmppLoginHandler = function(server,
35 username,
36 authToken,
37 needHandshakeBeforeTls,
38 sendMessageCallback,
39 startTlsCallback,
40 onHandshakeDoneCallback,
41 onErrorCallback) {
42 /** @private */
43 this.server_ = server;
44 /** @private */
45 this.username_ = username;
46 /** @private */
47 this.authToken_ = authToken;
48 /** @private */
49 this.needHandshakeBeforeTls_ = needHandshakeBeforeTls;
50 /** @private */
51 this.sendMessageCallback_ = sendMessageCallback;
52 /** @private */
53 this.startTlsCallback_ = startTlsCallback;
54 /** @private */
55 this.onHandshakeDoneCallback_ = onHandshakeDoneCallback;
56 /** @private */
57 this.onErrorCallback_ = onErrorCallback;
58
59 /** @private */
60 this.state_ = remoting.XmppLoginHandler.State.INIT;
61 /** @private */
62 this.jid_ = '';
63
64 /** @private {remoting.XmppStreamParser} */
65 this.streamParser_ = null;
66 }
67
68 /** @return {function(string, remoting.XmppStreamParser):void} */
69 remoting.XmppLoginHandler.prototype.getHandshakeDoneCallbackForTesting =
70 function() {
71 return this.onHandshakeDoneCallback_;
72 };
73
74 /**
75 * States the handshake goes through. States are iterated from INIT to DONE
76 * sequentially, except for ERROR state which may be accepted at any point.
77 *
78 * Following messages are sent/received in each state:
79 * INIT
80 * client -> server: Stream header
81 * client -> server: <starttls>
82 * WAIT_STREAM_HEADER
83 * client <- server: Stream header with list of supported features which
84 * should include starttls.
85 * WAIT_STARTTLS_RESPONSE
86 * client <- server: <proceed>
87 * STARTING_TLS
88 * TLS handshake
89 * client -> server: Stream header
90 * client -> server: <auth> message with the OAuth2 token.
91 * WAIT_STREAM_HEADER_AFTER_TLS
92 * client <- server: Stream header with list of supported authentication
93 * methods which is expected to include X-OAUTH2
94 * WAIT_AUTH_RESULT
95 * client <- server: <success> or <failure>
96 * client -> server: Stream header
97 * client -> server: <bind>
98 * client -> server: <iq><session/></iq> to start the session
99 * WAIT_STREAM_HEADER_AFTER_AUTH
100 * client <- server: Stream header with list of features that should
101 * include <bind>.
102 * WAIT_BIND_RESULT
103 * client <- server: <bind> result with JID.
104 * WAIT_SESSION_IQ_RESULT
105 * client <- server: result for <iq><session/></iq>
106 * DONE
107 *
108 * @enum {number}
109 */
110 remoting.XmppLoginHandler.State = {
111 INIT: 0,
112 WAIT_STREAM_HEADER: 1,
113 WAIT_STARTTLS_RESPONSE: 2,
114 STARTING_TLS: 3,
115 WAIT_STREAM_HEADER_AFTER_TLS: 4,
116 WAIT_AUTH_RESULT: 5,
117 WAIT_STREAM_HEADER_AFTER_AUTH: 6,
118 WAIT_BIND_RESULT: 7,
119 WAIT_SESSION_IQ_RESULT: 8,
120 DONE: 9,
121 ERROR: 10
122 };
123
124 remoting.XmppLoginHandler.prototype.start = function() {
125 if (this.needHandshakeBeforeTls_) {
126 this.state_ = remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER;
127 this.startStream_('<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>');
128 } else {
129 // If <starttls> handshake is not required then start TLS right away.
130 this.state_ = remoting.XmppLoginHandler.State.STARTING_TLS;
131 this.startTlsCallback_();
132 }
133 }
134
135 /** @param {ArrayBuffer} data */
136 remoting.XmppLoginHandler.prototype.onDataReceived = function(data) {
137 base.debug.assert(this.state_ != remoting.XmppLoginHandler.State.INIT &&
138 this.state_ != remoting.XmppLoginHandler.State.DONE &&
139 this.state_ != remoting.XmppLoginHandler.State.ERROR);
140
141 this.streamParser_.appendData(data);
142 }
143
144 /**
145 * @param {Element} stanza
146 * @private
147 */
148 remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
149 switch (this.state_) {
150 case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER:
151 if (stanza.querySelector('features>starttls')) {
152 this.state_ = remoting.XmppLoginHandler.State.WAIT_STARTTLS_RESPONSE;
153 } else {
154 this.onError_(
155 remoting.Error.unexpected(),
156 "Server doesn't support TLS.");
157 }
158 break;
159
160 case remoting.XmppLoginHandler.State.WAIT_STARTTLS_RESPONSE:
161 if (stanza.localName == "proceed") {
162 this.state_ = remoting.XmppLoginHandler.State.STARTING_TLS;
163 this.startTlsCallback_();
164 } else {
165 this.onError_(remoting.Error.unexpected(),
166 "Failed to start TLS: " +
167 (new XMLSerializer().serializeToString(stanza)));
168 }
169 break;
170
171 case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_TLS:
172 var mechanisms = Array.prototype.map.call(
173 stanza.querySelectorAll('features>mechanisms>mechanism'),
174 /** @param {Element} m */
175 function(m) { return m.textContent; });
176 if (mechanisms.indexOf("X-OAUTH2")) {
177 this.onError_(remoting.Error.unexpected(),
178 "OAuth2 is not supported by the server.");
179 return;
180 }
181
182 this.state_ = remoting.XmppLoginHandler.State.WAIT_AUTH_RESULT;
183
184 break;
185
186 case remoting.XmppLoginHandler.State.WAIT_AUTH_RESULT:
187 if (stanza.localName == 'success') {
188 this.state_ =
189 remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_AUTH;
190 this.startStream_(
191 '<iq type="set" id="0">' +
192 '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
193 '<resource>chromoting</resource>'+
194 '</bind>' +
195 '</iq>' +
196 '<iq type="set" id="1">' +
197 '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
198 '</iq>');
199 } else {
200 this.onError_(
201 new remoting.Error(remoting.Error.Tag.AUTHENTICATION_FAILED),
202 'Failed to authenticate: ' +
203 (new XMLSerializer().serializeToString(stanza)));
204 }
205 break;
206
207 case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_AUTH:
208 if (stanza.querySelector('features>bind')) {
209 this.state_ = remoting.XmppLoginHandler.State.WAIT_BIND_RESULT;
210 } else {
211 this.onError_(remoting.Error.unexpected(),
212 "Server doesn't support bind after authentication.");
213 }
214 break;
215
216 case remoting.XmppLoginHandler.State.WAIT_BIND_RESULT:
217 var jidElement = stanza.querySelector('iq>bind>jid');
218 if (stanza.getAttribute('id') != '0' ||
219 stanza.getAttribute('type') != 'result' || !jidElement) {
220 this.onError_(remoting.Error.unexpected(),
221 'Received unexpected response to bind: ' +
222 (new XMLSerializer().serializeToString(stanza)));
223 return;
224 }
225 this.jid_ = jidElement.textContent;
226 this.state_ = remoting.XmppLoginHandler.State.WAIT_SESSION_IQ_RESULT;
227 break;
228
229 case remoting.XmppLoginHandler.State.WAIT_SESSION_IQ_RESULT:
230 if (stanza.getAttribute('id') != '1' ||
231 stanza.getAttribute('type') != 'result') {
232 this.onError_(remoting.Error.unexpected(),
233 'Failed to start session: ' +
234 (new XMLSerializer().serializeToString(stanza)));
235 return;
236 }
237 this.state_ = remoting.XmppLoginHandler.State.DONE;
238 this.onHandshakeDoneCallback_(this.jid_, this.streamParser_);
239 break;
240
241 default:
242 base.debug.assert(false);
243 break;
244 }
245 }
246
247 remoting.XmppLoginHandler.prototype.onTlsStarted = function() {
248 base.debug.assert(this.state_ ==
249 remoting.XmppLoginHandler.State.STARTING_TLS);
250 this.state_ = remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_TLS;
251 var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_);
252
253 this.startStream_(
254 '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' +
255 'mechanism="X-OAUTH2" auth:service="oauth2" ' +
256 'auth:allow-generated-jid="true" ' +
257 'auth:client-uses-full-bind-result="true" ' +
258 'auth:allow-non-google-login="true" ' +
259 'xmlns:auth="http://www.google.com/talk/protocol/auth">' +
260 cookie +
261 '</auth>');
262 };
263
264 /**
265 * @param {string} text
266 * @private
267 */
268 remoting.XmppLoginHandler.prototype.onParserError_ = function(text) {
269 this.onError_(remoting.Error.unexpected(), text);
270 }
271
272 /**
273 * @param {string} firstMessage Message to send after stream header.
274 * @private
275 */
276 remoting.XmppLoginHandler.prototype.startStream_ = function(firstMessage) {
277 this.sendMessageCallback_('<stream:stream to="' + this.server_ +
278 '" version="1.0" xmlns="jabber:client" ' +
279 'xmlns:stream="http://etherx.jabber.org/streams">' +
280 firstMessage);
281 this.streamParser_ = new remoting.XmppStreamParser();
282 this.streamParser_.setCallbacks(this.onStanza_.bind(this),
283 this.onParserError_.bind(this));
284 }
285
286 /**
287 * @param {!remoting.Error} error
288 * @param {string} text
289 * @private
290 */
291 remoting.XmppLoginHandler.prototype.onError_ = function(error, text) {
292 if (this.state_ != remoting.XmppLoginHandler.State.ERROR) {
293 this.onErrorCallback_(error, text);
294 this.state_ = remoting.XmppLoginHandler.State.ERROR;
295 } else {
296 console.error(text);
297 }
298 }
OLDNEW
« no previous file with comments | « remoting/webapp/crd/js/xmpp_connection_unittest.js ('k') | remoting/webapp/crd/js/xmpp_login_handler_unittest.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698