summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remoting/webapp/unittests/xmpp_login_handler_unittest.js94
-rw-r--r--remoting/webapp/xmpp_login_handler.js127
2 files changed, 112 insertions, 109 deletions
diff --git a/remoting/webapp/unittests/xmpp_login_handler_unittest.js b/remoting/webapp/unittests/xmpp_login_handler_unittest.js
index 339ceba..8a74652 100644
--- a/remoting/webapp/unittests/xmpp_login_handler_unittest.js
+++ b/remoting/webapp/unittests/xmpp_login_handler_unittest.js
@@ -38,20 +38,23 @@ function handshakeBase() {
sinon.assert.calledWith(
sendMessage,
'<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' +
- 'xmlns:stream="http://etherx.jabber.org/streams">');
+ 'xmlns:stream="http://etherx.jabber.org/streams">' +
+ '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>');
sendMessage.reset();
loginHandler.onDataReceived(base.encodeUtf8(
'<stream:stream from="google.com" id="78A87C70559EF28A" version="1.0" ' +
- 'xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">' +
- '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">' +
- '<required/></starttls><mechanisms ' +
- 'xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' +
- '<mechanism>X-OAUTH2</mechanism><mechanism>X-GOOGLE-TOKEN</mechanism>' +
- '</mechanisms></stream:features>'));
- sinon.assert.calledWith(
- sendMessage, '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>');
- sendMessage.reset();
+ 'xmlns:stream="http://etherx.jabber.org/streams"' +
+ 'xmlns="jabber:client">' +
+ '<stream:features>' +
+ '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">' +
+ '<required/>' +
+ '</starttls>' +
+ '<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' +
+ '<mechanism>X-OAUTH2</mechanism>' +
+ '<mechanism>X-GOOGLE-TOKEN</mechanism>' +
+ '</mechanisms>' +
+ '</stream:features>'));
loginHandler.onDataReceived(
base.encodeUtf8('<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>'));
@@ -59,29 +62,30 @@ function handshakeBase() {
startTls.reset();
loginHandler.onTlsStarted();
- sinon.assert.calledWith(
- sendMessage,
- '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' +
- 'xmlns:stream="http://etherx.jabber.org/streams">');
- sendMessage.reset();
-
- loginHandler.onDataReceived(base.encodeUtf8(
- '<stream:stream from="google.com" id="DCDDE5171CB2154A" version="1.0" ' +
- 'xmlns:stream="http://etherx.jabber.org/streams" ' +
- 'xmlns="jabber:client"><stream:features>' +
- '<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' +
- '<mechanism>X-OAUTH2</mechanism><mechanism>X-GOOGLE-TOKEN</mechanism>' +
- '<mechanism>PLAIN</mechanism></mechanisms></stream:features>'));
var cookie = window.btoa("\0" + testUsername + "\0" + testToken);
sinon.assert.calledWith(
sendMessage,
+ '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' +
+ 'xmlns:stream="http://etherx.jabber.org/streams">' +
'<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-OAUTH2" ' +
'auth:service="oauth2" auth:allow-generated-jid="true" ' +
'auth:client-uses-full-bind-result="true" ' +
'auth:allow-non-google-login="true" ' +
'xmlns:auth="http://www.google.com/talk/protocol/auth">' + cookie +
- '</auth>');
+ '</auth>');
sendMessage.reset();
+
+ loginHandler.onDataReceived(base.encodeUtf8(
+ '<stream:stream from="google.com" id="DCDDE5171CB2154A" version="1.0" ' +
+ 'xmlns:stream="http://etherx.jabber.org/streams" ' +
+ 'xmlns="jabber:client">' +
+ '<stream:features>' +
+ '<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' +
+ '<mechanism>X-OAUTH2</mechanism>' +
+ '<mechanism>X-GOOGLE-TOKEN</mechanism>' +
+ '<mechanism>PLAIN</mechanism>' +
+ '</mechanisms>' +
+ '</stream:features>'));
}
test('should authenticate', function() {
@@ -92,32 +96,32 @@ test('should authenticate', function() {
sinon.assert.calledWith(
sendMessage,
'<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' +
- 'xmlns:stream="http://etherx.jabber.org/streams">');
+ 'xmlns:stream="http://etherx.jabber.org/streams">' +
+ '<iq type="set" id="0">' +
+ '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
+ '<resource>chromoting</resource>' +
+ '</bind>' +
+ '</iq>' +
+ '<iq type="set" id="1">' +
+ '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
+ '</iq>');
sendMessage.reset();
loginHandler.onDataReceived(base.encodeUtf8(
'<stream:stream from="google.com" id="104FA10576E2AA80" version="1.0" ' +
- 'xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">' +
- '<stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>' +
- '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
- '</stream:features>'));
- sinon.assert.calledWith(
- sendMessage,
- '<iq type="set" id="0"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
- '<resource>chromoting</resource></bind></iq>');
- sendMessage.reset();
-
- loginHandler.onDataReceived(
- base.encodeUtf8('<iq id="0" type="result">' +
- '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>' +
- testUsername + '/chromoting52B4920E</jid></bind></iq>'));
- sinon.assert.calledWith(
- sendMessage,
- '<iq type="set" id="1"><session ' +
- 'xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>');
- sendMessage.reset();
+ 'xmlns:stream="http://etherx.jabber.org/streams" ' +
+ 'xmlns="jabber:client">' +
+ '<stream:features>' +
+ '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>' +
+ '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
+ '</stream:features>' +
+ '<iq id="0" type="result">' +
+ '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
+ '<jid>' + testUsername + '/chromoting52B4920E</jid>' +
+ '</bind>' +
+ '</iq>' +
+ '<iq type="result" id="1"/>'));
- loginHandler.onDataReceived(base.encodeUtf8('<iq type="result" id="1"/>'));
sinon.assert.calledWith(onHandshakeDone);
});
diff --git a/remoting/webapp/xmpp_login_handler.js b/remoting/webapp/xmpp_login_handler.js
index bddc48e..f2034f4 100644
--- a/remoting/webapp/xmpp_login_handler.js
+++ b/remoting/webapp/xmpp_login_handler.js
@@ -66,53 +66,53 @@ remoting.XmppLoginHandler = function(server,
*
* Following messages are sent/received in each state:
* INIT
- * client -> server: Stream header
- * START_SENT
- * client <- server: Stream header with list of supported features which
- * should include starttls.
- * client -> server: <starttls>
- * STARTTLS_SENT
- * client <- server: <proceed>
+ * client -> server: Stream header
+ * client -> server: <starttls>
+ * WAIT_STREAM_HEADER
+ * client <- server: Stream header with list of supported features which
+ * should include starttls.
+ * WAIT_STARTTLS_RESPONSE
+ * client <- server: <proceed>
* STARTING_TLS
* TLS handshake
* client -> server: Stream header
- * START_SENT_AFTER_TLS
+ * client -> server: <auth> message with the OAuth2 token.
+ * WAIT_STREAM_HEADER_AFTER_TLS
* client <- server: Stream header with list of supported authentication
* methods which is expected to include X-OAUTH2
- * client -> server: <auth> message with the OAuth2 token.
- * AUTH_SENT
+ * WAIT_AUTH_RESULT
* client <- server: <success> or <failure>
* client -> server: Stream header
- * AUTH_ACCEPTED
+ * client -> server: <bind>
+ * client -> server: <iq><session/></iq> to start the session
+ * WAIT_STREAM_HEADER_AFTER_AUTH
* client <- server: Stream header with list of features that should
* include <bind>.
- * client -> server: <bind>
- * BIND_SENT
+ * WAIT_BIND_RESULT
* client <- server: <bind> result with JID.
- * client -> server: <iq><session/></iq> to start the session
- * SESSION_IQ_SENT
- * client <- server: iq result
+ * WAIT_SESSION_IQ_RESULT
+ * client <- server: result for <iq><session/></iq>
* DONE
*
* @enum {number}
*/
remoting.XmppLoginHandler.State = {
INIT: 0,
- START_SENT: 1,
- STARTTLS_SENT: 2,
+ WAIT_STREAM_HEADER: 1,
+ WAIT_STARTTLS_RESPONSE: 2,
STARTING_TLS: 3,
- START_SENT_AFTER_TLS: 4,
- AUTH_SENT: 5,
- AUTH_ACCEPTED: 6,
- BIND_SENT: 7,
- SESSION_IQ_SENT: 8,
+ WAIT_STREAM_HEADER_AFTER_TLS: 4,
+ WAIT_AUTH_RESULT: 5,
+ WAIT_STREAM_HEADER_AFTER_AUTH: 6,
+ WAIT_BIND_RESULT: 7,
+ WAIT_SESSION_IQ_RESULT: 8,
DONE: 9,
ERROR: 10
};
remoting.XmppLoginHandler.prototype.start = function() {
- this.state_ = remoting.XmppLoginHandler.State.START_SENT;
- this.startStream_();
+ this.state_ = remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER;
+ this.startStream_('<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>');
}
/** @param {ArrayBuffer} data */
@@ -130,17 +130,15 @@ remoting.XmppLoginHandler.prototype.onDataReceived = function(data) {
*/
remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
switch (this.state_) {
- case remoting.XmppLoginHandler.State.START_SENT:
+ case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER:
if (stanza.querySelector('features>starttls')) {
- this.sendMessageCallback_(
- '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>');
- this.state_ = remoting.XmppLoginHandler.State.STARTTLS_SENT;
+ this.state_ = remoting.XmppLoginHandler.State.WAIT_STARTTLS_RESPONSE;
} else {
this.onError_(remoting.Error.UNEXPECTED, "Server doesn't support TLS.");
}
break;
- case remoting.XmppLoginHandler.State.STARTTLS_SENT:
+ case remoting.XmppLoginHandler.State.WAIT_STARTTLS_RESPONSE:
if (stanza.localName == "proceed") {
this.state_ = remoting.XmppLoginHandler.State.STARTING_TLS;
this.startTlsCallback_();
@@ -151,7 +149,7 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
}
break;
- case remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS:
+ case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_TLS:
var mechanisms = Array.prototype.map.call(
stanza.querySelectorAll('features>mechanisms>mechanism'),
/** @param {Element} m */
@@ -162,24 +160,23 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
return;
}
- var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_);
+ this.state_ = remoting.XmppLoginHandler.State.WAIT_AUTH_RESULT;
- this.state_ = remoting.XmppLoginHandler.State.AUTH_SENT;
- this.sendMessageCallback_(
- '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' +
- 'mechanism="X-OAUTH2" auth:service="oauth2" ' +
- 'auth:allow-generated-jid="true" ' +
- 'auth:client-uses-full-bind-result="true" ' +
- 'auth:allow-non-google-login="true" ' +
- 'xmlns:auth="http://www.google.com/talk/protocol/auth">' +
- cookie +
- '</auth>');
break;
- case remoting.XmppLoginHandler.State.AUTH_SENT:
+ case remoting.XmppLoginHandler.State.WAIT_AUTH_RESULT:
if (stanza.localName == 'success') {
- this.state_ = remoting.XmppLoginHandler.State.AUTH_ACCEPTED;
- this.startStream_();
+ this.state_ =
+ remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_AUTH;
+ this.startStream_(
+ '<iq type="set" id="0">' +
+ '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
+ '<resource>chromoting</resource>'+
+ '</bind>' +
+ '</iq>' +
+ '<iq type="set" id="1">' +
+ '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
+ '</iq>');
} else {
this.onError_(remoting.Error.AUTHENTICATION_FAILED,
'Failed to authenticate: ' +
@@ -187,22 +184,16 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
}
break;
- case remoting.XmppLoginHandler.State.AUTH_ACCEPTED:
+ case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_AUTH:
if (stanza.querySelector('features>bind')) {
- this.sendMessageCallback_(
- '<iq type="set" id="0">' +
- '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
- '<resource>chromoting</resource>'+
- '</bind>' +
- '</iq>');
- this.state_ = remoting.XmppLoginHandler.State.BIND_SENT;
+ this.state_ = remoting.XmppLoginHandler.State.WAIT_BIND_RESULT;
} else {
this.onError_(remoting.Error.UNEXPECTED,
"Server doesn't support bind after authentication.");
}
break;
- case remoting.XmppLoginHandler.State.BIND_SENT:
+ case remoting.XmppLoginHandler.State.WAIT_BIND_RESULT:
var jidElement = stanza.querySelector('iq>bind>jid');
if (stanza.getAttribute('id') != '0' ||
stanza.getAttribute('type') != 'result' || !jidElement) {
@@ -212,14 +203,10 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
return;
}
this.jid_ = jidElement.textContent;
- this.sendMessageCallback_(
- '<iq type="set" id="1">' +
- '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
- '</iq>');
- this.state_ = remoting.XmppLoginHandler.State.SESSION_IQ_SENT;
+ this.state_ = remoting.XmppLoginHandler.State.WAIT_SESSION_IQ_RESULT;
break;
- case remoting.XmppLoginHandler.State.SESSION_IQ_SENT:
+ case remoting.XmppLoginHandler.State.WAIT_SESSION_IQ_RESULT:
if (stanza.getAttribute('id') != '1' ||
stanza.getAttribute('type') != 'result') {
this.onError_(remoting.Error.UNEXPECTED,
@@ -240,8 +227,18 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
remoting.XmppLoginHandler.prototype.onTlsStarted = function() {
base.debug.assert(this.state_ ==
remoting.XmppLoginHandler.State.STARTING_TLS);
- this.state_ = remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS;
- this.startStream_();
+ this.state_ = remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_TLS;
+ var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_);
+
+ this.startStream_(
+ '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' +
+ 'mechanism="X-OAUTH2" auth:service="oauth2" ' +
+ 'auth:allow-generated-jid="true" ' +
+ 'auth:client-uses-full-bind-result="true" ' +
+ 'auth:allow-non-google-login="true" ' +
+ 'xmlns:auth="http://www.google.com/talk/protocol/auth">' +
+ cookie +
+ '</auth>');
};
/**
@@ -253,12 +250,14 @@ remoting.XmppLoginHandler.prototype.onParserError_ = function(text) {
}
/**
+ * @param {string} firstMessage Message to send after stream header.
* @private
*/
-remoting.XmppLoginHandler.prototype.startStream_ = function() {
+remoting.XmppLoginHandler.prototype.startStream_ = function(firstMessage) {
this.sendMessageCallback_('<stream:stream to="' + this.server_ +
'" version="1.0" xmlns="jabber:client" ' +
- 'xmlns:stream="http://etherx.jabber.org/streams">');
+ 'xmlns:stream="http://etherx.jabber.org/streams">' +
+ firstMessage);
this.streamParser_ = new remoting.XmppStreamParser();
this.streamParser_.setCallbacks(this.onStanza_.bind(this),
this.onParserError_.bind(this));