summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/tests/mochitests/identity
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/tests/mochitests/identity')
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/identityPcTest.js79
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-bad.js1
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-min.js24
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-http-trick.js3
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-http-trick.js^headers^2
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-http.js3
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-http.js^headers^2
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-double.js3
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-double.js^headers^2
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-odd-path.js3
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-https-odd-path.js^headers^2
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-https.js3
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp-redirect-https.js^headers^2
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp.js119
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/idp.sjs18
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/login.html31
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/mochitest.ini47
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/test_fingerprints.html91
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/test_getIdentityAssertion.html101
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/test_idpproxy.html178
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/test_loginNeeded.html72
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/test_peerConnection_asymmetricIsolation.html31
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/test_peerConnection_peerIdentity.html21
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/test_setIdentityProvider.html67
-rw-r--r--dom/media/webrtc/tests/mochitests/identity/test_setIdentityProviderWithErrors.html57
25 files changed, 962 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..a4b2c55cee
--- /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..75390cbf4f
--- /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..75390cbf4f
--- /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..75390cbf4f
--- /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..75390cbf4f
--- /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..75390cbf4f
--- /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..557740657f
--- /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..7a60cc6c6e
--- /dev/null
+++ b/dom/media/webrtc/tests/mochitests/identity/mochitest.ini
@@ -0,0 +1,47 @@
+[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_fingerprints.html]
+scheme=https
+[test_getIdentityAssertion.html]
+[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_loginNeeded.html]
+support-files =
+ /.well-known/idp-proxy/login.html
+ /.well-known/idp-proxy/idp.sjs
+[test_peerConnection_asymmetricIsolation.html]
+scheme=https
+skip-if = os == 'android'
+[test_peerConnection_peerIdentity.html]
+scheme=https
+skip-if = os == 'android'
+[test_setIdentityProvider.html]
+scheme=https
+[test_setIdentityProviderWithErrors.html]
+scheme=https
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>