test_hawkrequest.js (6894B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { HAWKAuthenticatedRESTRequest, deriveHawkCredentials } = 7 ChromeUtils.importESModule("resource://services-common/hawkrequest.sys.mjs"); 8 const { Async } = ChromeUtils.importESModule( 9 "resource://services-common/async.sys.mjs" 10 ); 11 12 // https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol#wiki-use-session-certificatesign-etc 13 var SESSION_KEYS = { 14 sessionToken: h( 15 // eslint-disable-next-line no-useless-concat 16 "a0a1a2a3a4a5a6a7 a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7 b8b9babbbcbdbebf" 17 ), 18 19 tokenID: h( 20 // eslint-disable-next-line no-useless-concat 21 "c0a29dcf46174973 da1378696e4c82ae" + "10f723cf4f4d9f75 e39f4ae3851595ab" 22 ), 23 24 reqHMACkey: h( 25 // eslint-disable-next-line no-useless-concat 26 "9d8f22998ee7f579 8b887042466b72d5" + "3e56ab0c094388bf 65831f702d2febc0" 27 ), 28 }; 29 30 function do_register_cleanup() { 31 Services.prefs.clearUserPref("intl.accept_languages"); 32 Services.prefs.clearUserPref("services.common.log.logger.rest.request"); 33 34 // remove the pref change listener 35 let hawk = new HAWKAuthenticatedRESTRequest("https://example.com"); 36 hawk._intl.uninit(); 37 } 38 39 function run_test() { 40 registerCleanupFunction(do_register_cleanup); 41 42 Services.prefs.setStringPref( 43 "services.common.log.logger.rest.request", 44 "Trace" 45 ); 46 initTestLogging("Trace"); 47 48 run_next_test(); 49 } 50 51 add_test(function test_intl_accept_language() { 52 let testCount = 0; 53 let languages = [ 54 "zu-NP;vo", // Nepalese dialect of Zulu, defaulting to Volapük 55 "fa-CG;ik", // Congolese dialect of Farsei, defaulting to Inupiaq 56 ]; 57 58 function setLanguagePref(lang) { 59 Services.prefs.setStringPref("intl.accept_languages", lang); 60 } 61 62 let hawk = new HAWKAuthenticatedRESTRequest("https://example.com"); 63 64 Services.prefs.addObserver("intl.accept_languages", checkLanguagePref); 65 setLanguagePref(languages[testCount]); 66 67 function checkLanguagePref() { 68 CommonUtils.nextTick(function () { 69 // Ensure we're only called for the number of entries in languages[]. 70 Assert.less(testCount, languages.length); 71 72 Assert.equal(hawk._intl.accept_languages, languages[testCount]); 73 74 testCount++; 75 if (testCount < languages.length) { 76 // Set next language in prefs; Pref service will call checkNextLanguage. 77 setLanguagePref(languages[testCount]); 78 return; 79 } 80 81 // We've checked all the entries in languages[]. Cleanup and move on. 82 info( 83 "Checked " + 84 testCount + 85 " languages. Removing checkLanguagePref as pref observer." 86 ); 87 Services.prefs.removeObserver("intl.accept_languages", checkLanguagePref); 88 run_next_test(); 89 }); 90 } 91 }); 92 93 add_task(async function test_hawk_authenticated_request() { 94 let postData = { your: "data" }; 95 96 // An arbitrary date - Feb 2, 1971. It ends in a bunch of zeroes to make our 97 // computation with the hawk timestamp easier, since hawk throws away the 98 // millisecond values. 99 let then = 34329600000; 100 101 let clockSkew = 120000; 102 let timeOffset = -1 * clockSkew; 103 let localTime = then + clockSkew; 104 105 // Set the accept-languages pref to the Nepalese dialect of Zulu. 106 let acceptLanguage = "zu-NP"; // omit trailing ';', which our HTTP libs snip 107 Services.prefs.setStringPref("intl.accept_languages", acceptLanguage); 108 109 let credentials = { 110 id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", 111 key: "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=", 112 algorithm: "sha256", 113 }; 114 115 let server = httpd_setup({ 116 "/elysium": function (request, response) { 117 Assert.ok(request.hasHeader("Authorization")); 118 119 // check that the header timestamp is our arbitrary system date, not 120 // today's date. Note that hawk header timestamps are in seconds, not 121 // milliseconds. 122 let authorization = request.getHeader("Authorization"); 123 let tsMS = parseInt(/ts="(\d+)"/.exec(authorization)[1], 10) * 1000; 124 Assert.equal(tsMS, then); 125 126 // This testing can be a little wonky. In an environment where 127 // pref("intl.accept_languages") === 'en-US, en' 128 // the header is sent as: 129 // 'en-US,en;q=0.5' 130 // hence our fake value for acceptLanguage. 131 let lang = request.getHeader("Accept-Language"); 132 Assert.equal(lang, acceptLanguage); 133 134 let message = "yay"; 135 response.setStatusLine(request.httpVersion, 200, "OK"); 136 response.bodyOutputStream.write(message, message.length); 137 }, 138 }); 139 140 let url = server.baseURI + "/elysium"; 141 let extra = { 142 now: localTime, 143 localtimeOffsetMsec: timeOffset, 144 }; 145 146 let request = new HAWKAuthenticatedRESTRequest(url, credentials, extra); 147 148 // Allow hawk._intl to respond to the language pref change 149 await Async.promiseYield(); 150 151 await request.post(postData); 152 Assert.equal(200, request.response.status); 153 Assert.equal(request.response.body, "yay"); 154 155 Services.prefs.clearUserPref("intl.accept_languages"); 156 let pref = Services.locale.acceptLanguages; 157 Assert.notEqual(acceptLanguage, pref); 158 159 await promiseStopServer(server); 160 }); 161 162 add_task(async function test_hawk_language_pref_changed() { 163 let languages = [ 164 "zu-NP", // Nepalese dialect of Zulu 165 "fa-CG", // Congolese dialect of Farsi 166 ]; 167 168 let credentials = { 169 id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", 170 key: "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=", 171 algorithm: "sha256", 172 }; 173 174 function setLanguage(lang) { 175 Services.prefs.setStringPref("intl.accept_languages", lang); 176 } 177 178 let server = httpd_setup({ 179 "/foo": function (request, response) { 180 Assert.equal(languages[1], request.getHeader("Accept-Language")); 181 182 response.setStatusLine(request.httpVersion, 200, "OK"); 183 }, 184 }); 185 186 let url = server.baseURI + "/foo"; 187 let request; 188 189 setLanguage(languages[0]); 190 191 // A new request should create the stateful object for tracking the current 192 // language. 193 request = new HAWKAuthenticatedRESTRequest(url, credentials); 194 195 // Wait for change to propagate 196 await Async.promiseYield(); 197 Assert.equal(languages[0], request._intl.accept_languages); 198 199 // Change the language pref ... 200 setLanguage(languages[1]); 201 202 await Async.promiseYield(); 203 204 request = new HAWKAuthenticatedRESTRequest(url, credentials); 205 let response = await request.post({}); 206 207 Assert.equal(200, response.status); 208 Services.prefs.clearUserPref("intl.accept_languages"); 209 210 await promiseStopServer(server); 211 }); 212 213 add_task(async function test_deriveHawkCredentials() { 214 let credentials = await deriveHawkCredentials( 215 SESSION_KEYS.sessionToken, 216 "sessionToken" 217 ); 218 Assert.equal(credentials.id, SESSION_KEYS.tokenID); 219 Assert.equal( 220 CommonUtils.bytesAsHex(credentials.key), 221 SESSION_KEYS.reqHMACkey 222 ); 223 }); 224 225 // turn formatted test vectors into normal hex strings 226 function h(hexStr) { 227 return hexStr.replace(/\s+/g, ""); 228 }