services/common/hawkrequest.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
michael@0 8
michael@0 9 this.EXPORTED_SYMBOLS = [
michael@0 10 "HAWKAuthenticatedRESTRequest",
michael@0 11 ];
michael@0 12
michael@0 13 Cu.import("resource://gre/modules/Preferences.jsm");
michael@0 14 Cu.import("resource://gre/modules/Services.jsm");
michael@0 15 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 16 Cu.import("resource://gre/modules/Log.jsm");
michael@0 17 Cu.import("resource://services-common/rest.js");
michael@0 18
michael@0 19 XPCOMUtils.defineLazyModuleGetter(this, "CryptoUtils",
michael@0 20 "resource://services-crypto/utils.js");
michael@0 21
michael@0 22 const Prefs = new Preferences("services.common.rest.");
michael@0 23
michael@0 24 /**
michael@0 25 * Single-use HAWK-authenticated HTTP requests to RESTish resources.
michael@0 26 *
michael@0 27 * @param uri
michael@0 28 * (String) URI for the RESTRequest constructor
michael@0 29 *
michael@0 30 * @param credentials
michael@0 31 * (Object) Optional credentials for computing HAWK authentication
michael@0 32 * header.
michael@0 33 *
michael@0 34 * @param payloadObj
michael@0 35 * (Object) Optional object to be converted to JSON payload
michael@0 36 *
michael@0 37 * @param extra
michael@0 38 * (Object) Optional extra params for HAWK header computation.
michael@0 39 * Valid properties are:
michael@0 40 *
michael@0 41 * now: <current time in milliseconds>,
michael@0 42 * localtimeOffsetMsec: <local clock offset vs server>
michael@0 43 *
michael@0 44 * extra.localtimeOffsetMsec is the value in milliseconds that must be added to
michael@0 45 * the local clock to make it agree with the server's clock. For instance, if
michael@0 46 * the local clock is two minutes ahead of the server, the time offset in
michael@0 47 * milliseconds will be -120000.
michael@0 48 */
michael@0 49
michael@0 50 this.HAWKAuthenticatedRESTRequest =
michael@0 51 function HawkAuthenticatedRESTRequest(uri, credentials, extra={}) {
michael@0 52 RESTRequest.call(this, uri);
michael@0 53
michael@0 54 this.credentials = credentials;
michael@0 55 this.now = extra.now || Date.now();
michael@0 56 this.localtimeOffsetMsec = extra.localtimeOffsetMsec || 0;
michael@0 57 this._log.trace("local time, offset: " + this.now + ", " + (this.localtimeOffsetMsec));
michael@0 58
michael@0 59 // Expose for testing
michael@0 60 this._intl = getIntl();
michael@0 61 };
michael@0 62 HAWKAuthenticatedRESTRequest.prototype = {
michael@0 63 __proto__: RESTRequest.prototype,
michael@0 64
michael@0 65 dispatch: function dispatch(method, data, onComplete, onProgress) {
michael@0 66 let contentType = "text/plain";
michael@0 67 if (method == "POST" || method == "PUT") {
michael@0 68 contentType = "application/json";
michael@0 69 }
michael@0 70 if (this.credentials) {
michael@0 71 let options = {
michael@0 72 now: this.now,
michael@0 73 localtimeOffsetMsec: this.localtimeOffsetMsec,
michael@0 74 credentials: this.credentials,
michael@0 75 payload: data && JSON.stringify(data) || "",
michael@0 76 contentType: contentType,
michael@0 77 };
michael@0 78 let header = CryptoUtils.computeHAWK(this.uri, method, options);
michael@0 79 this.setHeader("Authorization", header.field);
michael@0 80 this._log.trace("hawk auth header: " + header.field);
michael@0 81 }
michael@0 82
michael@0 83 this.setHeader("Content-Type", contentType);
michael@0 84
michael@0 85 this.setHeader("Accept-Language", this._intl.accept_languages);
michael@0 86
michael@0 87 return RESTRequest.prototype.dispatch.call(
michael@0 88 this, method, data, onComplete, onProgress
michael@0 89 );
michael@0 90 }
michael@0 91 };
michael@0 92
michael@0 93 // With hawk request, we send the user's accepted-languages with each request.
michael@0 94 // To keep the number of times we read this pref at a minimum, maintain the
michael@0 95 // preference in a stateful object that notices and updates itself when the
michael@0 96 // pref is changed.
michael@0 97 this.Intl = function Intl() {
michael@0 98 // We won't actually query the pref until the first time we need it
michael@0 99 this._accepted = "";
michael@0 100 this._everRead = false;
michael@0 101 this._log = Log.repository.getLogger("Services.common.RESTRequest");
michael@0 102 this._log.level = Log.Level[Prefs.get("log.logger.rest.request")];
michael@0 103 this.init();
michael@0 104 };
michael@0 105
michael@0 106 this.Intl.prototype = {
michael@0 107 init: function() {
michael@0 108 Services.prefs.addObserver("intl.accept_languages", this, false);
michael@0 109 },
michael@0 110
michael@0 111 uninit: function() {
michael@0 112 Services.prefs.removeObserver("intl.accept_languages", this);
michael@0 113 },
michael@0 114
michael@0 115 observe: function(subject, topic, data) {
michael@0 116 this.readPref();
michael@0 117 },
michael@0 118
michael@0 119 readPref: function() {
michael@0 120 this._everRead = true;
michael@0 121 try {
michael@0 122 this._accepted = Services.prefs.getComplexValue(
michael@0 123 "intl.accept_languages", Ci.nsIPrefLocalizedString).data;
michael@0 124 } catch (err) {
michael@0 125 this._log.error("Error reading intl.accept_languages pref: " + CommonUtils.exceptionStr(err));
michael@0 126 }
michael@0 127 },
michael@0 128
michael@0 129 get accept_languages() {
michael@0 130 if (!this._everRead) {
michael@0 131 this.readPref();
michael@0 132 }
michael@0 133 return this._accepted;
michael@0 134 },
michael@0 135 };
michael@0 136
michael@0 137 // Singleton getter for Intl, creating an instance only when we first need it.
michael@0 138 let intl = null;
michael@0 139 function getIntl() {
michael@0 140 if (!intl) {
michael@0 141 intl = new Intl();
michael@0 142 }
michael@0 143 return intl;
michael@0 144 }
michael@0 145

mercurial