diff options
Diffstat (limited to 'dom/media/webrtc/tests/mochitests/identity')
25 files changed, 963 insertions, 0 deletions
diff --git a/dom/media/webrtc/tests/mochitests/identity/identityPcTest.js b/dom/media/webrtc/tests/mochitests/identity/identityPcTest.js new file mode 100644 index 0000000000..1381873f9d --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/identityPcTest.js @@ -0,0 +1,79 @@ +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", { protocol: "idp.js" }); + test.pcRemote.setIdentityProvider("test2.example.com", { + protocol: "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]; + for (const track of remoteStream.getTracks()) { + mustThrowWith( + `Freshly received ${track.kind} track with peerIdentity`, + "SecurityError", + () => new MediaRecorder(new MediaStream([track])).start() + ); + } + return Promise.all([ + audioIsSilence(true, remoteStream), + videoIsBlack(true, remoteStream), + ]); + }, + ]); + return test.run(); +} diff --git a/dom/media/webrtc/tests/mochitests/identity/idp-bad.js b/dom/media/webrtc/tests/mochitests/identity/idp-bad.js new file mode 100644 index 0000000000..86e1cb7a34 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/idp-bad.js @@ -0,0 +1 @@ +<This isn't valid JS> diff --git a/dom/media/webrtc/tests/mochitests/identity/idp-min.js b/dom/media/webrtc/tests/mochitests/identity/idp-min.js new file mode 100644 index 0000000000..6a45bf044a --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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(payload, origin, usernameHint) { + dump("idp: generateAssertion(" + payload + ")\n"); + return Promise.resolve({ + idp: { domain: "example.com", protocol: "idp.js" }, + assertion: "bogus", + }); + }, + + validateAssertion(assertion, origin) { + dump("idp: validateAssertion(" + assertion + ")\n"); + return Promise.resolve({ + identity: "user@example.com", + contents: "bogus", + }); + }, + }); +})(this); diff --git a/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http-trick.js b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http-trick.js new file mode 100644 index 0000000000..240b98bbda --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http-trick.js @@ -0,0 +1,3 @@ +(function() { + dump("ERROR\n"); +})(); diff --git a/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http-trick.js^headers^ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http-trick.js^headers^ new file mode 100644 index 0000000000..b3a2afd90a --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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/webrtc/tests/mochitests/identity/idp-redirect-http.js b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http.js new file mode 100644 index 0000000000..240b98bbda --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http.js @@ -0,0 +1,3 @@ +(function() { + dump("ERROR\n"); +})(); diff --git a/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http.js^headers^ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-http.js^headers^ new file mode 100644 index 0000000000..d2380984e7 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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/webrtc/tests/mochitests/identity/idp-redirect-https-double.js b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-double.js new file mode 100644 index 0000000000..240b98bbda --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-double.js @@ -0,0 +1,3 @@ +(function() { + dump("ERROR\n"); +})(); diff --git a/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-double.js^headers^ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-double.js^headers^ new file mode 100644 index 0000000000..3fb8a35ae7 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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/webrtc/tests/mochitests/identity/idp-redirect-https-odd-path.js b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-odd-path.js new file mode 100644 index 0000000000..240b98bbda --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-odd-path.js @@ -0,0 +1,3 @@ +(function() { + dump("ERROR\n"); +})(); diff --git a/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-odd-path.js^headers^ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-odd-path.js^headers^ new file mode 100644 index 0000000000..6e2931eda9 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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/webrtc/tests/mochitests/identity/idp-redirect-https.js b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https.js new file mode 100644 index 0000000000..240b98bbda --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https.js @@ -0,0 +1,3 @@ +(function() { + dump("ERROR\n"); +})(); diff --git a/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https.js^headers^ b/dom/media/webrtc/tests/mochitests/identity/idp-redirect-https.js^headers^ new file mode 100644 index 0000000000..77d56ac442 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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/webrtc/tests/mochitests/identity/idp.js b/dom/media/webrtc/tests/mochitests/identity/idp.js new file mode 100644 index 0000000000..d779f7fa5e --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/idp.js @@ -0,0 +1,119 @@ +(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() { + return fetch( + "https://example.com/.well-known/idp-proxy/idp.sjs?" + this.id + ).then(response => response.status === 200); + }, + checkLogin(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(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(usernameHint) { + dump("_selectUsername: usernameHint(" + usernameHint + ")\n"); + 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(payload, origin, options) { + dump( + "idp: generateAssertion(" + + payload + + ", " + + origin + + ", " + + JSON.stringify(options) + + ")\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(options.usernameHint), + contents: payload, + }), + }); + }, + + validateAssertion(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/webrtc/tests/mochitests/identity/idp.sjs b/dom/media/webrtc/tests/mochitests/identity/idp.sjs new file mode 100644 index 0000000000..e1a245be78 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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/webrtc/tests/mochitests/identity/login.html b/dom/media/webrtc/tests/mochitests/identity/login.html new file mode 100644 index 0000000000..eafba22f2d --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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/webrtc/tests/mochitests/identity/mochitest.ini b/dom/media/webrtc/tests/mochitests/identity/mochitest.ini new file mode 100644 index 0000000000..14fceb62cd --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/mochitest.ini @@ -0,0 +1,48 @@ +[DEFAULT] +subsuite = media +skip-if = (os == 'linux' && !debug) +support-files = + /.well-known/idp-proxy/idp.js + identityPcTest.js + !/dom/media/webrtc/tests/mochitests/blacksilence.js + !/dom/media/webrtc/tests/mochitests/dataChannel.js + !/dom/media/webrtc/tests/mochitests/head.js + !/dom/media/webrtc/tests/mochitests/network.js + !/dom/media/webrtc/tests/mochitests/pc.js + !/dom/media/webrtc/tests/mochitests/sdpUtils.js + !/dom/media/webrtc/tests/mochitests/templates.js + !/dom/media/webrtc/tests/mochitests/turnConfig.js +tags = mtg + +[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] +scheme=https +[test_getIdentityAssertion.html] +[test_setIdentityProvider.html] +scheme=https +[test_setIdentityProviderWithErrors.html] +scheme=https +[test_peerConnection_peerIdentity.html] +scheme=https +skip-if = os == 'android' +[test_peerConnection_asymmetricIsolation.html] +scheme=https +skip-if = os == 'android' +[test_loginNeeded.html] +support-files = + /.well-known/idp-proxy/login.html + /.well-known/idp-proxy/idp.sjs diff --git a/dom/media/webrtc/tests/mochitests/identity/test_fingerprints.html b/dom/media/webrtc/tests/mochitests/identity/test_fingerprints.html new file mode 100644 index 0000000000..0a7f0a2033 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/test_fingerprints.html @@ -0,0 +1,91 @@ +<html> +<head> +<meta charset="utf-8" /> +<script type="application/javascript">var scriptRelativePath = "../";</script> +<script type="application/javascript" src="../pc.js"></script> +</head> +<body> +<script class="testbody" type="application/javascript"> +createHTML({ title: "Test multiple identity fingerprints", bug: "1005152" }); + +// here we call the identity provider directly +async function getIdentityAssertion(fingerprint) { + const { IdpSandbox } = SpecialPowers.ChromeUtils.import( + 'resource://gre/modules/media/IdpSandbox.jsm' + ); + const sandbox = new IdpSandbox('example.com', 'idp.js', window); + const idp = SpecialPowers.wrap(await sandbox.start()); + const assertion = SpecialPowers.wrap(await + idp.generateAssertion(JSON.stringify({ fingerprint }), + 'https://example.com', + {})); + const assertionString = btoa(JSON.stringify(assertion)); + sandbox.stop(); + return assertionString; +} + +// This takes a real fingerprint and makes some extra bad ones. +function makeFingerprints(algorithm, digest) { + const fingerprints = []; + fingerprints.push({ algorithm, digest }); + for (var i = 0; i < 3; ++i) { + fingerprints.push({ + algorithm, + digest: digest.replace(/:./g, ':' + i.toString(16)) + }); + } + return fingerprints; +} + +const fingerprintRegex = /^a=fingerprint:(\S+) (\S+)/m; +const 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. + +runNetworkTest(async () => { + // this one fails setRemoteDescription if the identity is not good + const pcStrict = new RTCPeerConnection({ peerIdentity: 'someone@example.com'}); + // this one will be manually tweaked to have two fingerprints + const pcDouble = new RTCPeerConnection({}); + + const stream = await getUserMedia({ video: true }); + ok(stream, 'Got test stream'); + const [track] = stream.getTracks(); + pcDouble.addTrack(track, stream); + try { + const offer = await pcDouble.createOffer(); + ok(offer, 'Got offer'); + const match = offer.sdp.match(fingerprintRegex); + if (!match) { + throw new Error('No fingerprint in offer SDP'); + } + const fingerprints = makeFingerprints(match[1], match[2]); + const assertion = await getIdentityAssertion(fingerprints); + ok(assertion, 'Should have assertion'); + + const sdp = offer.sdp.slice(0, match.index) + + 'a=identity:' + assertion + '\n' + + fingerprintSdp(fingerprints.slice(1)) + + offer.sdp.slice(match.index); + + await pcStrict.setRemoteDescription({ type: 'offer', sdp }); + ok(true, 'Modified fingerprints were accepted'); + } catch (error) { + const e = SpecialPowers.wrap(error); + ok(false, 'error in test: ' + + (e.message ? (e.message + '\n' + e.stack) : e)); + } + pcStrict.close(); + pcDouble.close(); + track.stop(); +}); +</script> +</body> +</html> diff --git a/dom/media/webrtc/tests/mochitests/identity/test_getIdentityAssertion.html b/dom/media/webrtc/tests/mochitests/identity/test_getIdentityAssertion.html new file mode 100644 index 0000000000..47e1cb1df6 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/test_getIdentityAssertion.html @@ -0,0 +1,101 @@ +<!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) { + dump('instructions: ' + instructions + '\n'); + dump('userHint: ' + userHint + '\n'); + t.pcLocal.setIdentityProvider('example.com', + { protocol: 'idp.js' + instructions, + usernameHint: 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', + { protocol: 'idp-bad.js', + usernameHint: '' }); + 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')); + } + ]); + return test.run(); +} +runNetworkTest(theTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webrtc/tests/mochitests/identity/test_idpproxy.html b/dom/media/webrtc/tests/mochitests/identity/test_idpproxy.html new file mode 100644 index 0000000000..065501b8a4 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/test_idpproxy.html @@ -0,0 +1,178 @@ +<html> +<head> +<meta charset="utf-8" /> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> + <script class="testbody" type="application/javascript"> +"use strict"; +var { IdpSandbox } = SpecialPowers.ChromeUtils.import( + "resource://gre/modules/media/IdpSandbox.jsm" +); +var dummyPayload = JSON.stringify({ + this: 'is', + a: ['stu', 6], + obj: null +}); + +function test_domain_sandbox() { + var diabolical = { + toString() { + 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', + { usernameHint: '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/webrtc/tests/mochitests/identity/test_loginNeeded.html b/dom/media/webrtc/tests/mochitests/identity/test_loginNeeded.html new file mode 100644 index 0000000000..550dc20d92 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/test_loginNeeded.html @@ -0,0 +1,72 @@ +<!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(); + }); + }); +} + +function checkLogin(t, name, onLoginNeeded) { + t.pcLocal.setIdentityProvider('example.com', + { protocol: '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'); + }); + } + ]); + return test.run(); +} +runNetworkTest(theTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webrtc/tests/mochitests/identity/test_peerConnection_asymmetricIsolation.html b/dom/media/webrtc/tests/mochitests/identity/test_peerConnection_asymmetricIsolation.html new file mode 100644 index 0000000000..65a2fc5392 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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. + return identityPcTest({ + audio: true, + video: true + }); +} +runNetworkTest(theTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webrtc/tests/mochitests/identity/test_peerConnection_peerIdentity.html b/dom/media/webrtc/tests/mochitests/identity/test_peerConnection_peerIdentity.html new file mode 100644 index 0000000000..a8116cc451 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/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/webrtc/tests/mochitests/identity/test_setIdentityProvider.html b/dom/media/webrtc/tests/mochitests/identity/test_setIdentityProvider.html new file mode 100644 index 0000000000..ac7cba6a5e --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/test_setIdentityProvider.html @@ -0,0 +1,67 @@ +<!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", + { protocol: "idp.js", + usernameHint: "someone" }); + test.pcRemote.setIdentityProvider("test2.example.com", + { protocol: "idp.js", + usernameHinte: "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"); + } + ]); + return test.run(); +} +runNetworkTest(theTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webrtc/tests/mochitests/identity/test_setIdentityProviderWithErrors.html b/dom/media/webrtc/tests/mochitests/identity/test_setIdentityProviderWithErrors.html new file mode 100644 index 0000000000..ce6832d1e6 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/identity/test_setIdentityProviderWithErrors.html @@ -0,0 +1,57 @@ +<!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', + { protocol: 'idp.js#bad-validate', + usernameHint: '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')) + ]); + } + ]); + + return test.run(); +}); + +</script> +</pre> +</body> +</html> |