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 (function() { 6 7 'use strict'; 8 9 var testUsername = 'testUsername (a] gmail.com'; 10 var testToken = 'testToken'; 11 12 var sendMessage = null; 13 var startTls = null; 14 var onHandshakeDone = null; 15 var onStanzaStr = null; 16 var onError = null; 17 var loginHandler = null; 18 19 module('XmppLoginHandler', { 20 setup: function() { 21 sendMessage = sinon.spy(); 22 startTls = sinon.spy(); 23 onHandshakeDone = sinon.spy(); 24 onStanzaStr = sinon.spy(); 25 onError = sinon.spy(); 26 function onStanza(stanza) { 27 onStanzaStr(new XMLSerializer().serializeToString(stanza)); 28 } 29 loginHandler = new remoting.XmppLoginHandler( 30 'google.com', testUsername, testToken, sendMessage, startTls, 31 onHandshakeDone, onError); 32 } 33 }); 34 35 // Executes handshake base. 36 function handshakeBase() { 37 loginHandler.start(); 38 sinon.assert.calledWith( 39 sendMessage, 40 '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' + 41 'xmlns:stream="http://etherx.jabber.org/streams">' + 42 '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>'); 43 sendMessage.reset(); 44 45 loginHandler.onDataReceived(base.encodeUtf8( 46 '<stream:stream from="google.com" id="78A87C70559EF28A" version="1.0" ' + 47 'xmlns:stream="http://etherx.jabber.org/streams"' + 48 'xmlns="jabber:client">' + 49 '<stream:features>' + 50 '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">' + 51 '<required/>' + 52 '</starttls>' + 53 '<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' + 54 '<mechanism>X-OAUTH2</mechanism>' + 55 '<mechanism>X-GOOGLE-TOKEN</mechanism>' + 56 '</mechanisms>' + 57 '</stream:features>')); 58 59 loginHandler.onDataReceived( 60 base.encodeUtf8('<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>')); 61 sinon.assert.calledWith(startTls); 62 startTls.reset(); 63 64 loginHandler.onTlsStarted(); 65 var cookie = window.btoa("\0" + testUsername + "\0" + testToken); 66 sinon.assert.calledWith( 67 sendMessage, 68 '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' + 69 'xmlns:stream="http://etherx.jabber.org/streams">' + 70 '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-OAUTH2" ' + 71 'auth:service="oauth2" auth:allow-generated-jid="true" ' + 72 'auth:client-uses-full-bind-result="true" ' + 73 'auth:allow-non-google-login="true" ' + 74 'xmlns:auth="http://www.google.com/talk/protocol/auth">' + cookie + 75 '</auth>'); 76 sendMessage.reset(); 77 78 loginHandler.onDataReceived(base.encodeUtf8( 79 '<stream:stream from="google.com" id="DCDDE5171CB2154A" version="1.0" ' + 80 'xmlns:stream="http://etherx.jabber.org/streams" ' + 81 'xmlns="jabber:client">' + 82 '<stream:features>' + 83 '<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' + 84 '<mechanism>X-OAUTH2</mechanism>' + 85 '<mechanism>X-GOOGLE-TOKEN</mechanism>' + 86 '<mechanism>PLAIN</mechanism>' + 87 '</mechanisms>' + 88 '</stream:features>')); 89 } 90 91 test('should authenticate', function() { 92 handshakeBase(); 93 94 loginHandler.onDataReceived( 95 base.encodeUtf8('<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>')); 96 sinon.assert.calledWith( 97 sendMessage, 98 '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' + 99 'xmlns:stream="http://etherx.jabber.org/streams">' + 100 '<iq type="set" id="0">' + 101 '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' + 102 '<resource>chromoting</resource>' + 103 '</bind>' + 104 '</iq>' + 105 '<iq type="set" id="1">' + 106 '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' + 107 '</iq>'); 108 sendMessage.reset(); 109 110 loginHandler.onDataReceived(base.encodeUtf8( 111 '<stream:stream from="google.com" id="104FA10576E2AA80" version="1.0" ' + 112 'xmlns:stream="http://etherx.jabber.org/streams" ' + 113 'xmlns="jabber:client">' + 114 '<stream:features>' + 115 '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>' + 116 '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' + 117 '</stream:features>' + 118 '<iq id="0" type="result">' + 119 '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' + 120 '<jid>' + testUsername + '/chromoting52B4920E</jid>' + 121 '</bind>' + 122 '</iq>' + 123 '<iq type="result" id="1"/>')); 124 125 sinon.assert.calledWith(onHandshakeDone); 126 }); 127 128 test('should return AUTHENTICATION_FAILED error when failed to authenticate', 129 function() { 130 handshakeBase(); 131 132 loginHandler.onDataReceived( 133 base.encodeUtf8('<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' + 134 '<not-authorized/></failure>')); 135 sinon.assert.calledWith(onError, remoting.Error.AUTHENTICATION_FAILED); 136 }); 137 138 test('should return UNEXPECTED error when failed to parse stream', 139 function() { 140 loginHandler.start(); 141 loginHandler.onDataReceived( 142 base.encodeUtf8('BAD DATA')); 143 sinon.assert.calledWith(onError, remoting.Error.UNEXPECTED); 144 }); 145 146 })(); 147