diff options
Diffstat (limited to 'dom/media/tests/mochitest/identity')
25 files changed, 929 insertions, 0 deletions
diff --git a/dom/media/tests/mochitest/identity/identityPcTest.js b/dom/media/tests/mochitest/identity/identityPcTest.js new file mode 100644 index 000000000..e5fcbc5db --- /dev/null +++ b/dom/media/tests/mochitest/identity/identityPcTest.js @@ -0,0 +1,53 @@ +function identityPcTest(remoteOptions) { + var user = 'someone'; + var domain1 = 'test1.example.com'; + var domain2 = 'test2.example.com'; + var id1 = user + '@' + domain1; + var id2 = user + '@' + domain2; + + test = new PeerConnectionTest({ + config_local: { + peerIdentity: id2 + }, + config_remote: { + peerIdentity: id1 + } + }); + test.setMediaConstraints([{ + audio: true, + video: true, + peerIdentity: id2 + }], [remoteOptions || { + audio: true, + video: true, + peerIdentity: id1 + }]); + test.pcLocal.setIdentityProvider('test1.example.com', 'idp.js'); + test.pcRemote.setIdentityProvider('test2.example.com', 'idp.js'); + test.chain.append([ + function PEER_IDENTITY_IS_SET_CORRECTLY(test) { + // no need to wait to check identity in this case, + // setRemoteDescription should wait for the IdP to complete + function checkIdentity(pc, pfx, idp, name) { + return pc.peerIdentity.then(peerInfo => { + is(peerInfo.idp, idp, pfx + "IdP check"); + is(peerInfo.name, name + "@" + idp, pfx + "identity check"); + }); + } + + return Promise.all([ + checkIdentity(test.pcLocal._pc, "local: ", "test2.example.com", "someone"), + checkIdentity(test.pcRemote._pc, "remote: ", "test1.example.com", "someone") + ]); + }, + + function REMOTE_STREAMS_ARE_RESTRICTED(test) { + var remoteStream = test.pcLocal._pc.getRemoteStreams()[0]; + return Promise.all([ + audioIsSilence(true, remoteStream), + videoIsBlack(true, remoteStream) + ]); + } + ]); + test.run(); +} diff --git a/dom/media/tests/mochitest/identity/idp-bad.js b/dom/media/tests/mochitest/identity/idp-bad.js new file mode 100644 index 000000000..86e1cb7a3 --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-bad.js @@ -0,0 +1 @@ +<This isn't valid JS> diff --git a/dom/media/tests/mochitest/identity/idp-min.js b/dom/media/tests/mochitest/identity/idp-min.js new file mode 100644 index 000000000..8e228b688 --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-min.js @@ -0,0 +1,24 @@ +(function(global) { + 'use strict'; + // A minimal implementation of the interface. + // Though this isn't particularly functional. + // This is needed so that we can have a "working" IdP served + // from two different locations in the tree. + global.rtcIdentityProvider.register({ + generateAssertion: function(payload, origin, usernameHint) { + dump('idp: generateAssertion(' + payload + ')\n'); + return Promise.resolve({ + idp: { domain: 'example.com', protocol: 'idp.js' }, + assertion: 'bogus' + }); + }, + + validateAssertion: function(assertion, origin) { + dump('idp: validateAssertion(' + assertion + ')\n'); + return Promise.resolve({ + identity: 'user@example.com', + contents: 'bogus' + }); + } + }); +}(this)); diff --git a/dom/media/tests/mochitest/identity/idp-redirect-http-trick.js b/dom/media/tests/mochitest/identity/idp-redirect-http-trick.js new file mode 100644 index 000000000..5d35ac0ca --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-http-trick.js @@ -0,0 +1,3 @@ +(function() { + dump('ERROR\n'); +}()); diff --git a/dom/media/tests/mochitest/identity/idp-redirect-http-trick.js^headers^ b/dom/media/tests/mochitest/identity/idp-redirect-http-trick.js^headers^ new file mode 100644 index 000000000..b3a2afd90 --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-http-trick.js^headers^ @@ -0,0 +1,2 @@ +HTTP 301 Moved Permanently +Location: http://example.com/.well-known/idp-proxy/idp-redirect-https.js diff --git a/dom/media/tests/mochitest/identity/idp-redirect-http.js b/dom/media/tests/mochitest/identity/idp-redirect-http.js new file mode 100644 index 000000000..5d35ac0ca --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-http.js @@ -0,0 +1,3 @@ +(function() { + dump('ERROR\n'); +}()); diff --git a/dom/media/tests/mochitest/identity/idp-redirect-http.js^headers^ b/dom/media/tests/mochitest/identity/idp-redirect-http.js^headers^ new file mode 100644 index 000000000..d2380984e --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-http.js^headers^ @@ -0,0 +1,2 @@ +HTTP 301 Moved Permanently +Location: http://example.com/.well-known/idp-proxy/idp.js diff --git a/dom/media/tests/mochitest/identity/idp-redirect-https-double.js b/dom/media/tests/mochitest/identity/idp-redirect-https-double.js new file mode 100644 index 000000000..5d35ac0ca --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-https-double.js @@ -0,0 +1,3 @@ +(function() { + dump('ERROR\n'); +}()); diff --git a/dom/media/tests/mochitest/identity/idp-redirect-https-double.js^headers^ b/dom/media/tests/mochitest/identity/idp-redirect-https-double.js^headers^ new file mode 100644 index 000000000..3fb8a35ae --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-https-double.js^headers^ @@ -0,0 +1,2 @@ +HTTP 301 Moved Permanently +Location: https://example.com/.well-known/idp-proxy/idp-redirect-https.js diff --git a/dom/media/tests/mochitest/identity/idp-redirect-https-odd-path.js b/dom/media/tests/mochitest/identity/idp-redirect-https-odd-path.js new file mode 100644 index 000000000..5d35ac0ca --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-https-odd-path.js @@ -0,0 +1,3 @@ +(function() { + dump('ERROR\n'); +}()); diff --git a/dom/media/tests/mochitest/identity/idp-redirect-https-odd-path.js^headers^ b/dom/media/tests/mochitest/identity/idp-redirect-https-odd-path.js^headers^ new file mode 100644 index 000000000..6e2931eda --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-https-odd-path.js^headers^ @@ -0,0 +1,2 @@ +HTTP 301 Moved Permanently +Location: https://example.com/.well-known/idp-min.js diff --git a/dom/media/tests/mochitest/identity/idp-redirect-https.js b/dom/media/tests/mochitest/identity/idp-redirect-https.js new file mode 100644 index 000000000..5d35ac0ca --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-https.js @@ -0,0 +1,3 @@ +(function() { + dump('ERROR\n'); +}()); diff --git a/dom/media/tests/mochitest/identity/idp-redirect-https.js^headers^ b/dom/media/tests/mochitest/identity/idp-redirect-https.js^headers^ new file mode 100644 index 000000000..77d56ac44 --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp-redirect-https.js^headers^ @@ -0,0 +1,2 @@ +HTTP 301 Moved Permanently +Location: https://example.com/.well-known/idp-proxy/idp.js diff --git a/dom/media/tests/mochitest/identity/idp.js b/dom/media/tests/mochitest/identity/idp.js new file mode 100644 index 000000000..6cc3a1706 --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp.js @@ -0,0 +1,110 @@ +(function(global) { + 'use strict'; + + // rather than create a million different IdP configurations and litter the + // world with files all containing near-identical code, let's use the hash/URL + // fragment as a way of generating instructions for the IdP + var instructions = global.location.hash.replace('#', '').split(':'); + function is(target) { + return function(instruction) { + return instruction === target; + }; + } + + function IDPJS() { + this.domain = global.location.host; + var path = global.location.pathname; + this.protocol = + path.substring(path.lastIndexOf('/') + 1) + global.location.hash; + this.id = crypto.getRandomValues(new Uint8Array(10)).join('.'); + } + + IDPJS.prototype = { + getLogin: function() { + return fetch('https://example.com/.well-known/idp-proxy/idp.sjs?' + this.id) + .then(response => response.status === 200); + }, + checkLogin: function(result) { + return this.getLogin() + .then(loggedIn => { + if (loggedIn) { + return result; + } + return Promise.reject({ + name: 'IdpLoginError', + loginUrl: 'https://example.com/.well-known/idp-proxy/login.html#' + + this.id + }); + }); + }, + + borkResult: function(result) { + if (instructions.some(is('throw'))) { + throw new Error('Throwing!'); + } + if (instructions.some(is('fail'))) { + return Promise.reject(new Error('Failing!')); + } + if (instructions.some(is('login'))) { + return this.checkLogin(result); + } + if (instructions.some(is('hang'))) { + return new Promise(r => {}); + } + dump('idp: result=' + JSON.stringify(result) + '\n'); + return Promise.resolve(result); + }, + + _selectUsername: function(usernameHint) { + var username = 'someone@' + this.domain; + if (usernameHint) { + var at = usernameHint.indexOf('@'); + if (at < 0) { + username = usernameHint + '@' + this.domain; + } else if (usernameHint.substring(at + 1) === this.domain) { + username = usernameHint; + } + } + return username; + }, + + generateAssertion: function(payload, origin, usernameHint) { + dump('idp: generateAssertion(' + payload + ')\n'); + var idpDetails = { + domain: this.domain, + protocol: this.protocol + }; + if (instructions.some(is('bad-assert'))) { + idpDetails = {}; + } + return this.borkResult({ + idp: idpDetails, + assertion: JSON.stringify({ + username: this._selectUsername(usernameHint), + contents: payload + }) + }); + }, + + validateAssertion: function(assertion, origin) { + dump('idp: validateAssertion(' + assertion + ')\n'); + var assertion = JSON.parse(assertion); + if (instructions.some(is('bad-validate'))) { + assertion.contents = {}; + } + return this.borkResult({ + identity: assertion.username, + contents: assertion.contents + }); + } + }; + + if (!instructions.some(is('not_ready'))) { + dump('registering idp.js' + global.location.hash + '\n'); + var idp = new IDPJS(); + global.rtcIdentityProvider.register({ + generateAssertion: idp.generateAssertion.bind(idp), + validateAssertion: idp.validateAssertion.bind(idp) + }); + } +}(this)); diff --git a/dom/media/tests/mochitest/identity/idp.sjs b/dom/media/tests/mochitest/identity/idp.sjs new file mode 100644 index 000000000..b6313297b --- /dev/null +++ b/dom/media/tests/mochitest/identity/idp.sjs @@ -0,0 +1,18 @@ +function handleRequest(request, response) { + var key = '/.well-known/idp-proxy/' + request.queryString; + dump(getState(key) + '\n'); + if (request.method === 'GET') { + if (getState(key)) { + response.setStatusLine(request.httpVersion, 200, 'OK'); + } else { + response.setStatusLine(request.httpVersion, 404, 'Not Found'); + } + } else if (request.method === 'PUT') { + setState(key, 'OK'); + response.setStatusLine(request.httpVersion, 200, 'OK'); + } else { + response.setStatusLine(request.httpVersion, 406, 'Method Not Allowed'); + } + response.setHeader('Content-Type', 'text/plain;charset=UTF-8'); + response.write('OK'); +} diff --git a/dom/media/tests/mochitest/identity/login.html b/dom/media/tests/mochitest/identity/login.html new file mode 100644 index 000000000..eafba22f2 --- /dev/null +++ b/dom/media/tests/mochitest/identity/login.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Identity Provider Login</title> + <script type="application/javascript"> + window.onload = () => { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", "https://example.com/.well-known/idp-proxy/idp.sjs?" + + window.location.hash.replace('#', '')); + xhr.onload = () => { + var isFramed = (window !== window.top); + var parent = isFramed ? window.parent : window.opener; + // Using '*' is cheating, but that's OK. + parent.postMessage('LOGINDONE', '*'); + var done = document.createElement('div'); + + done.textContent = 'Done'; + document.body.appendChild(done); + + if (!isFramed) { + window.close(); + } + }; + xhr.send(); + }; + </script> +</head> +<body> + <div>Logging in...</div> +</body> +</html> diff --git a/dom/media/tests/mochitest/identity/mochitest.ini b/dom/media/tests/mochitest/identity/mochitest.ini new file mode 100644 index 000000000..85f01083d --- /dev/null +++ b/dom/media/tests/mochitest/identity/mochitest.ini @@ -0,0 +1,42 @@ +[DEFAULT] +# Android 4.3 - bug 981881 +subsuite = media +skip-if = android_version == '18' || (os == 'linux' && !debug && !e10s) +support-files = + /.well-known/idp-proxy/idp.js + identityPcTest.js + !/dom/media/tests/mochitest/blacksilence.js + !/dom/media/tests/mochitest/dataChannel.js + !/dom/media/tests/mochitest/head.js + !/dom/media/tests/mochitest/network.js + !/dom/media/tests/mochitest/pc.js + !/dom/media/tests/mochitest/sdpUtils.js + !/dom/media/tests/mochitest/templates.js + !/dom/media/tests/mochitest/turnConfig.js +tags = msg + +[test_idpproxy.html] +support-files = + /.well-known/idp-proxy/idp-redirect-http.js + /.well-known/idp-proxy/idp-redirect-http.js^headers^ + /.well-known/idp-proxy/idp-redirect-http-trick.js + /.well-known/idp-proxy/idp-redirect-http-trick.js^headers^ + /.well-known/idp-proxy/idp-redirect-https.js + /.well-known/idp-proxy/idp-redirect-https.js^headers^ + /.well-known/idp-proxy/idp-redirect-https-double.js + /.well-known/idp-proxy/idp-redirect-https-double.js^headers^ + /.well-known/idp-proxy/idp-redirect-https-odd-path.js + /.well-known/idp-proxy/idp-redirect-https-odd-path.js^headers^ + /.well-known/idp-min.js + /.well-known/idp-proxy/idp-bad.js + +[test_fingerprints.html] +[test_getIdentityAssertion.html] +[test_setIdentityProvider.html] +[test_setIdentityProviderWithErrors.html] +[test_peerConnection_peerIdentity.html] +[test_peerConnection_asymmetricIsolation.html] +[test_loginNeeded.html] +support-files = + /.well-known/idp-proxy/login.html + /.well-known/idp-proxy/idp.sjs diff --git a/dom/media/tests/mochitest/identity/test_fingerprints.html b/dom/media/tests/mochitest/identity/test_fingerprints.html new file mode 100644 index 000000000..0fd065af2 --- /dev/null +++ b/dom/media/tests/mochitest/identity/test_fingerprints.html @@ -0,0 +1,112 @@ +<html> +<head> +<meta charset="utf-8" /> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> + <script class="testbody" type="application/javascript"> +'use strict'; + +// here we call the identity provider directly +function getIdentityAssertion(fpArray) { + var Cu = SpecialPowers.Cu; + var rtcid = Cu.import('resource://gre/modules/media/IdpSandbox.jsm'); + var sandbox = new rtcid.IdpSandbox('example.com', 'idp.js', window); + return sandbox.start() + .then(idp => SpecialPowers.wrap(idp) + .generateAssertion(JSON.stringify({ fingerprint: fpArray }), + 'https://example.com')) + .then(assertion => { + assertion = SpecialPowers.wrap(assertion); + var assertionString = btoa(JSON.stringify(assertion)); + sandbox.stop(); + return assertionString; + }); +} + +// This takes a real fingerprint and makes some extra bad ones. +function makeFingerprints(algo, digest) { + var fingerprints = []; + fingerprints.push({ algorithm: algo, digest: digest }); + for (var i = 0; i < 3; ++i) { + fingerprints.push({ + algorithm: algo, + digest: digest.replace(/:./g, ':' + i.toString(16)) + }); + } + return fingerprints; +} + +var fingerprintRegex = /^a=fingerprint:(\S+) (\S+)/m; +var identityRegex = /^a=identity:(\S+)/m; + +function fingerprintSdp(fingerprints) { + return fingerprints.map(fp => 'a=fInGeRpRiNt:' + fp.algorithm + + ' ' + fp.digest + '\n').join(''); +} + +// Firefox only uses a single fingerprint. +// That doesn't mean we have it create SDP that describes two. +// This function synthesizes that SDP and tries to set it. +function testMultipleFingerprints() { + // this one fails setRemoteDescription if the identity is not good + var pcStrict = new RTCPeerConnection({ peerIdentity: 'someone@example.com'}); + // this one will be manually tweaked to have two fingerprints + var pcDouble = new RTCPeerConnection({}); + + var offer, match, fingerprints; + + var fail = msg => + (e => ok(false, 'error in ' + msg + ': ' + + (e.message ? (e.message + '\n' + e.stack) : e))); + + navigator.mediaDevices.getUserMedia({ audio: true, fake: true }) + .then(stream => { + ok(stream, 'Got fake stream'); + pcDouble.addStream(stream); + return pcDouble.createOffer(); + }) + .then(o => { + offer = o; + ok(offer, 'Got offer'); + + match = offer.sdp.match(fingerprintRegex); + if (!match) { + throw new Error('No fingerprint in offer SDP'); + } + fingerprints = makeFingerprints(match[1], match[2]); + return getIdentityAssertion(fingerprints); + }) + .then(assertion => { + ok(assertion, 'Should have assertion'); + + var sdp = offer.sdp.slice(0, match.index) + + 'a=identity:' + assertion + '\n' + + fingerprintSdp(fingerprints.slice(1)) + + offer.sdp.slice(match.index); + + var desc = new RTCSessionDescription({ type: 'offer', sdp: sdp }); + return pcStrict.setRemoteDescription(desc); + }) + .then(() => { + ok(true, 'Modified fingerprints were accepted'); + }, error => { + var e = SpecialPowers.wrap(error); + ok(false, 'error in test: ' + + (e.message ? (e.message + '\n' + e.stack) : e)); + }) + .then(() => { + pcStrict.close(); + pcDouble.close(); + SimpleTest.finish(); + }); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({ + set: [ [ 'media.peerconnection.identity.enabled', true ] ] +}, testMultipleFingerprints); +</script> + </body> +</html> diff --git a/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html b/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html new file mode 100644 index 000000000..662981613 --- /dev/null +++ b/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html @@ -0,0 +1,96 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript">var scriptRelativePath = "../";</script> + <script type="application/javascript" src="../pc.js"></script> +</head> +<body> +<pre id="test"> +<script type="application/javascript"> + createHTML({ + title: "getIdentityAssertion Tests", + bug: "942367" + }); + +function checkIdentity(assertion, identity) { + // here we dig into the payload, which means we need to know something + // about how the IdP actually works (not good in general, but OK here) + var assertion = JSON.parse(atob(assertion)).assertion; + var user = JSON.parse(assertion).username; + is(user, identity, 'id should be "' + identity + '" is "' + user + '"'); +} + +function getAssertion(t, instructions, userHint) { + t.pcLocal.setIdentityProvider('example.com', 'idp.js' + instructions, + userHint); + return t.pcLocal._pc.getIdentityAssertion(); +} + +var test; +function theTest() { + test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.chain.removeAfter('PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE'); + test.chain.append([ + function PC_LOCAL_IDENTITY_ASSERTION_FAILS_WITHOUT_PROVIDER(t) { + return t.pcLocal._pc.getIdentityAssertion() + .then(a => ok(false, 'should fail without provider'), + e => ok(e, 'should fail without provider')); + }, + + function PC_LOCAL_IDENTITY_ASSERTION_FAILS_WITH_BAD_PROVIDER(t) { + t.pcLocal._pc.setIdentityProvider('example.com', 'idp-bad.js', ''); + return t.pcLocal._pc.getIdentityAssertion() + .then(a => ok(false, 'should fail with bad provider'), + e => { + is(e.name, 'IdpError', 'should fail with bad provider'); + ok(e.message, 'should include a nice message'); + }); + }, + + function PC_LOCAL_GET_TWO_ASSERTIONS(t) { + return Promise.all([ + getAssertion(t, ''), + getAssertion(t, '') + ]).then(assertions => { + is(assertions.length, 2, "Two assertions generated"); + assertions.forEach(a => checkIdentity(a, 'someone@example.com')); + }); + }, + + function PC_LOCAL_IDP_FAILS(t) { + return getAssertion(t, '#fail') + .then(a => ok(false, '#fail should not get an identity result'), + e => is(e.name, 'IdpError', '#fail should cause rejection')); + }, + + function PC_LOCAL_IDP_LOGIN_ERROR(t) { + return getAssertion(t, '#login') + .then(a => ok(false, '#login should not work'), + e => { + is(e.name, 'IdpLoginError', 'name is IdpLoginError'); + is(t.pcLocal._pc.idpLoginUrl.split('#')[0], + 'https://example.com/.well-known/idp-proxy/login.html', + 'got the right login URL from the IdP'); + }); + }, + + function PC_LOCAL_IDP_NOT_READY(t) { + return getAssertion(t, '#not_ready') + .then(a => ok(false, '#not_ready should not get an identity result'), + e => is(e.name, 'IdpError', '#not_ready should cause rejection')); + }, + + function PC_LOCAL_ASSERTION_WITH_SPECIFIC_NAME(t) { + return getAssertion(t, '', 'user@example.com') + .then(a => checkIdentity(a, 'user@example.com')); + } + ]); + test.run(); +} +runNetworkTest(theTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/tests/mochitest/identity/test_idpproxy.html b/dom/media/tests/mochitest/identity/test_idpproxy.html new file mode 100644 index 000000000..c8860d1f4 --- /dev/null +++ b/dom/media/tests/mochitest/identity/test_idpproxy.html @@ -0,0 +1,176 @@ +<html> +<head> +<meta charset="utf-8" /> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> + <script class="testbody" type="application/javascript"> +"use strict"; +var Cu = SpecialPowers.Cu; +var rtcid = Cu.import("resource://gre/modules/media/IdpSandbox.jsm"); +var IdpSandbox = rtcid.IdpSandbox; +var dummyPayload = JSON.stringify({ + this: 'is', + a: ['stu', 6], + obj: null +}); + +function test_domain_sandbox() { + var diabolical = { + toString : function() { + return 'example.com/path'; + } + }; + var domains = [ 'ex/foo', 'user@ex', 'user:pass@ex', 'ex#foo', 'ex?foo', + '', 12, null, diabolical, true ]; + domains.forEach(function(domain) { + try { + var idp = new IdpSandbox(domain, undefined, window); + ok(false, 'IdpSandbox allowed a bad domain: ' + domain); + } catch (e) { + var str = (typeof domain === 'string') ? domain : typeof domain; + ok(true, 'Evil domain "' + str + '" raises exception'); + } + }); +} + +function test_protocol_sandbox() { + var protos = [ '../evil/proto', '..%2Fevil%2Fproto', + '\\evil', '%5cevil', 12, true, {} ]; + protos.forEach(function(proto) { + try { + var idp = new IdpSandbox('example.com', proto, window); + ok(false, 'IdpSandbox allowed a bad protocol: ' + proto); + } catch (e) { + var str = (typeof proto === 'string') ? proto : typeof proto; + ok(true, 'Evil protocol "' + proto + '" raises exception'); + } + }); +} + +function idpName(hash) { + return 'idp.js' + (hash ? ('#' + hash) : ''); +} + +function makeSandbox(js) { + var name = js || idpName(); + info('Creating a sandbox for the protocol: ' + name); + var sandbox = new IdpSandbox('example.com', name, window); + return sandbox.start().then(idp => SpecialPowers.wrap(idp)); +} + +function test_generate_assertion() { + return makeSandbox() + .then(idp => idp.generateAssertion(dummyPayload, + 'https://example.net')) + .then(response => { + response = SpecialPowers.wrap(response); + is(response.idp.domain, 'example.com', 'domain is correct'); + is(response.idp.protocol, 'idp.js', 'protocol is correct'); + ok(typeof response.assertion === 'string', 'assertion is present'); + }); +} + +// test that the test IdP can eat its own dogfood; which is the only way to test +// validateAssertion, since that consumes the output of generateAssertion (in +// theory, generateAssertion could identify a different IdP domain). + +function test_validate_assertion() { + return makeSandbox() + .then(idp => idp.generateAssertion(dummyPayload, + 'https://example.net', 'user')) + .then(assertion => { + var wrapped = SpecialPowers.wrap(assertion); + return makeSandbox() + .then(idp => idp.validateAssertion(wrapped.assertion, + 'https://example.net')); + }).then(response => { + response = SpecialPowers.wrap(response); + is(response.identity, 'user@example.com'); + is(response.contents, dummyPayload); + }); +} + +// We don't want to test the #bad or the #hang instructions, +// errors of the sort those generate aren't handled by the sandbox code. +function test_assertion_failure(reason) { + return () => { + return makeSandbox(idpName(reason)) + .then(idp => idp.generateAssertion('hello', 'example.net')) + .then(r => ok(false, 'should not succeed on ' + reason), + e => ok(true, 'failed correctly on ' + reason)); + }; +} + +function test_load_failure() { + return makeSandbox('non-existent-file') + .then(() => ok(false, 'Should fail to load non-existent file'), + e => ok(e, 'Should fail to load non-existent file')); +} + +function test_redirect_ok(from) { + return () => { + return makeSandbox(from) + .then(idp => idp.generateAssertion('hello', 'example.net')) + .then(r => ok(SpecialPowers.wrap(r).assertion, + 'Redirect to https should be OK')); + }; +} + +function test_redirect_fail(from) { + return () => { + return makeSandbox(from) + .then(() => ok(false, 'Redirect to https should fail'), + e => ok(e, 'Redirect to https should fail')); + }; +} + +function test_bad_js() { + return makeSandbox('idp-bad.js') + .then(() => ok(false, 'Bad JS should not load'), + e => ok(e, 'Bad JS should not load')); +} + +function run_all_tests() { + [ + test_domain_sandbox, + test_protocol_sandbox, + test_generate_assertion, + test_validate_assertion, + + // fail of the IdP fails + test_assertion_failure('fail'), + // fail if the IdP throws + test_assertion_failure('throw'), + // fail if the IdP is not ready + test_assertion_failure('not_ready'), + + test_load_failure(), + // Test a redirect to an HTTPS origin, which should be OK + test_redirect_ok('idp-redirect-https.js'), + // Two redirects is fine too + test_redirect_ok('idp-redirect-https-double.js'), + // A secure redirect to a path other than /.well-known/idp-proxy/* should + // also work fine. + test_redirect_ok('idp-redirect-https-odd-path.js'), + // A redirect to HTTP is not-cool + test_redirect_fail('idp-redirect-http.js'), + // Also catch tricks like https->http->https + test_redirect_fail('idp-redirect-http-trick.js'), + + test_bad_js + ].reduce((p, test) => { + return p.then(test) + .catch(e => ok(false, test.name + ' failed: ' + + SpecialPowers.wrap(e).message + '\n' + + SpecialPowers.wrap(e).stack)); + }, Promise.resolve()) + .then(() => SimpleTest.finish()); +} + +SimpleTest.waitForExplicitFinish(); +run_all_tests(); +</script> + </body> +</html> diff --git a/dom/media/tests/mochitest/identity/test_loginNeeded.html b/dom/media/tests/mochitest/identity/test_loginNeeded.html new file mode 100644 index 000000000..4c95a7978 --- /dev/null +++ b/dom/media/tests/mochitest/identity/test_loginNeeded.html @@ -0,0 +1,71 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript">var scriptRelativePath = "../";</script> + <script type="application/javascript" src="../pc.js"></script> +</head> +<body> +<pre id="test"> +<script type="application/javascript"> + createHTML({ + title: 'RTCPeerConnection identity with login', + bug: '1153314' + }); + +function waitForLoginDone() { + return new Promise(resolve => { + window.addEventListener('message', function listener(e) { + is(e.origin, 'https://example.com', 'got the right message origin'); + is(e.data, 'LOGINDONE', 'got the right message'); + window.removeEventListener('message', listener); + resolve(); + }, false); + }); +} + +function checkLogin(t, name, onLoginNeeded) { + t.pcLocal.setIdentityProvider('example.com', 'idp.js#login:' + name); + return t.pcLocal._pc.getIdentityAssertion() + .then(a => ok(false, 'should request login'), + e => { + is(e.name, 'IdpLoginError', 'name is IdpLoginError'); + is(t.pcLocal._pc.idpLoginUrl.split('#')[0], + 'https://example.com/.well-known/idp-proxy/login.html', + 'got the right login URL from the IdP'); + return t.pcLocal._pc.idpLoginUrl; + }) + .then(onLoginNeeded) + .then(waitForLoginDone) + .then(() => t.pcLocal._pc.getIdentityAssertion()) + .then(a => ok(a, 'got assertion')); +} + +function theTest() { + var test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.chain.removeAfter('PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE'); + test.chain.append([ + function PC_LOCAL_IDENTITY_ASSERTION_WITH_IFRAME_LOGIN(t) { + return checkLogin(t, 'iframe', loginUrl => { + var iframe = document.createElement('iframe'); + iframe.setAttribute('src', loginUrl); + iframe.frameBorder = 0; + iframe.width = 400; + iframe.height = 60; + document.getElementById('display').appendChild(iframe); + }); + }, + function PC_LOCAL_IDENTITY_ASSERTION_WITH_WINDOW_LOGIN(t) { + return checkLogin(t, 'openwin', loginUrl => { + window.open(loginUrl, 'login', 'width=400,height=60'); + }); + } + ]); + test.run(); +} +runNetworkTest(theTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/tests/mochitest/identity/test_peerConnection_asymmetricIsolation.html b/dom/media/tests/mochitest/identity/test_peerConnection_asymmetricIsolation.html new file mode 100644 index 000000000..5ea0f5c77 --- /dev/null +++ b/dom/media/tests/mochitest/identity/test_peerConnection_asymmetricIsolation.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript">var scriptRelativePath = "../";</script> + <script type="application/javascript" src="../pc.js"></script> + <script type="application/javascript" src="../blacksilence.js"></script> + <script type="application/javascript" src="identityPcTest.js"></script> +</head> +<body> +<pre id="test"> +<script type="application/javascript"> +createHTML({ + title: "Non-isolated media entering an isolated session becomes isolated", + bug: "996238" +}); + +function theTest() { + // Override the remote media capture options to remove isolation for the + // remote party; the test verifies that the media it receives on the local + // side is isolated anyway. + identityPcTest({ + audio: true, + video: true + }); +} +runNetworkTest(theTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html b/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html new file mode 100644 index 000000000..a8116cc45 --- /dev/null +++ b/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript">var scriptRelativePath = "../";</script> + <script type="application/javascript" src="../pc.js"></script> + <script type="application/javascript" src="../blacksilence.js"></script> + <script type="application/javascript" src="identityPcTest.js"></script> +</head> +<body> +<pre id="test"> +<script type="application/javascript"> +createHTML({ + title: "setIdentityProvider leads to peerIdentity and assertions in SDP", + bug: "942367" +}); + +runNetworkTest(identityPcTest); +</script> +</pre> +</body> +</html> diff --git a/dom/media/tests/mochitest/identity/test_setIdentityProvider.html b/dom/media/tests/mochitest/identity/test_setIdentityProvider.html new file mode 100644 index 000000000..ee65d5af5 --- /dev/null +++ b/dom/media/tests/mochitest/identity/test_setIdentityProvider.html @@ -0,0 +1,63 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript">var scriptRelativePath = "../";</script> + <script type="application/javascript" src="../pc.js"></script> +</head> +<body> +<pre id="test"> +<script type="application/javascript"> + createHTML({ + title: "setIdentityProvider leads to peerIdentity and assertions in SDP", + bug: "942367" + }); + +function checkIdentity(peer, prefix, idp, name) { + prefix = prefix + ": "; + return peer._pc.peerIdentity.then(peerIdentity => { + ok(peerIdentity, prefix + "peerIdentity is set"); + is(peerIdentity.idp, idp, prefix + "IdP is correct"); + is(peerIdentity.name, name + "@" + idp, prefix + "identity is correct"); + }); +} + +function theTest() { + var test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.pcLocal.setIdentityProvider("test1.example.com", "idp.js", "someone"); + test.pcRemote.setIdentityProvider("test2.example.com", "idp.js", "someone"); + + test.chain.append([ + function PC_LOCAL_PEER_IDENTITY_IS_SET_CORRECTLY(test) { + return checkIdentity(test.pcLocal, "local", "test2.example.com", "someone"); + }, + function PC_REMOTE_PEER_IDENTITY_IS_SET_CORRECTLY(test) { + return checkIdentity(test.pcRemote, "remote", "test1.example.com", "someone"); + }, + + function OFFER_AND_ANSWER_INCLUDES_IDENTITY(test) { + ok(test.originalOffer.sdp.includes("a=identity"), "a=identity is in the offer SDP"); + ok(test.originalAnswer.sdp.includes("a=identity"), "a=identity is in the answer SDP"); + }, + + function PC_LOCAL_DESCRIPTIONS_CONTAIN_IDENTITY(test) { + ok(test.pcLocal.localDescription.sdp.includes("a=identity"), + "a=identity is in the local copy of the offer"); + ok(test.pcLocal.remoteDescription.sdp.includes("a=identity"), + "a=identity is in the local copy of the answer"); + }, + function PC_REMOTE_DESCRIPTIONS_CONTAIN_IDENTITY(test) { + ok(test.pcRemote.localDescription.sdp.includes("a=identity"), + "a=identity is in the remote copy of the offer"); + ok(test.pcRemote.remoteDescription.sdp.includes("a=identity"), + "a=identity is in the remote copy of the answer"); + } + ]); + test.run(); +} +runNetworkTest(theTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html b/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html new file mode 100644 index 000000000..ce6276a2b --- /dev/null +++ b/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript">var scriptRelativePath = "../";</script> + <script type="application/javascript" src="../pc.js"></script> +</head> +<body> +<pre id="test"> +<script type="application/javascript"> +'use strict'; + createHTML({ + title: "Identity Provider returning errors is handled correctly", + bug: "942367" + }); + +runNetworkTest(function () { + var test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + // No IdP for local. + // Remote generates a bad assertion, but that only fails to validate + test.pcRemote.setIdentityProvider('example.com', 'idp.js#bad-validate', 'nobody'); + + // Save the peerIdentity promises now, since when they reject they are + // replaced and we expect them to be rejected this time + var peerIdentityLocal = test.pcLocal._pc.peerIdentity; + var peerIdentityRemote = test.pcRemote._pc.peerIdentity; + + test.chain.append([ + function ONLY_REMOTE_SDP_INCLUDES_IDENTITY_ASSERTION(t) { + ok(!t.originalOffer.sdp.includes('a=identity'), + 'a=identity not contained in the offer SDP'); + ok(t.originalAnswer.sdp.includes('a=identity'), + 'a=identity is contained in the answer SDP'); + }, + function PEER_IDENTITY_IS_EMPTY(t) { + // we are only waiting for the local side to complete + // an error on the remote side is immediately fatal though + return Promise.race([ + peerIdentityLocal.then( + () => ok(false, t.pcLocal + ' incorrectly received valid peer identity'), + e => ok(e, t.pcLocal + ' correctly failed to validate peer identity')), + peerIdentityRemote.then( + () => ok(false, t.pcRemote + ' incorrecly received a valid peer identity'), + e => ok(false, t.pcRemote + ' incorrectly rejected peer identity')) + ]); + } + ]); + + test.run(); +}); + +</script> +</pre> +</body> +</html> |