dom/identity/DOMIdentity.jsm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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} = Components;
michael@0 8
michael@0 9 Cu.import("resource://gre/modules/Services.jsm");
michael@0 10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 11
michael@0 12 const PREF_FXA_ENABLED = "identity.fxaccounts.enabled";
michael@0 13
michael@0 14 // This is the parent process corresponding to nsDOMIdentity.
michael@0 15 this.EXPORTED_SYMBOLS = ["DOMIdentity"];
michael@0 16
michael@0 17 XPCOMUtils.defineLazyModuleGetter(this, "objectCopy",
michael@0 18 "resource://gre/modules/identity/IdentityUtils.jsm");
michael@0 19
michael@0 20 XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
michael@0 21 #ifdef MOZ_B2G_VERSION
michael@0 22 "resource://gre/modules/identity/MinimalIdentity.jsm");
michael@0 23 #else
michael@0 24 "resource://gre/modules/identity/Identity.jsm");
michael@0 25 #endif
michael@0 26
michael@0 27 XPCOMUtils.defineLazyModuleGetter(this, "FirefoxAccounts",
michael@0 28 "resource://gre/modules/identity/FirefoxAccounts.jsm");
michael@0 29
michael@0 30 XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject",
michael@0 31 "resource://gre/modules/identity/IdentityUtils.jsm");
michael@0 32
michael@0 33 XPCOMUtils.defineLazyModuleGetter(this,
michael@0 34 "Logger",
michael@0 35 "resource://gre/modules/identity/LogUtils.jsm");
michael@0 36
michael@0 37 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
michael@0 38 "@mozilla.org/parentprocessmessagemanager;1",
michael@0 39 "nsIMessageListenerManager");
michael@0 40
michael@0 41 function log(...aMessageArgs) {
michael@0 42 Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
michael@0 43 }
michael@0 44
michael@0 45 function IDDOMMessage(aOptions) {
michael@0 46 objectCopy(aOptions, this);
michael@0 47 }
michael@0 48
michael@0 49 function IDPProvisioningContext(aID, aOrigin, aTargetMM) {
michael@0 50 this._id = aID;
michael@0 51 this._origin = aOrigin;
michael@0 52 this._mm = aTargetMM;
michael@0 53 }
michael@0 54
michael@0 55 IDPProvisioningContext.prototype = {
michael@0 56 get id() this._id,
michael@0 57 get origin() this._origin,
michael@0 58
michael@0 59 doBeginProvisioningCallback: function IDPPC_doBeginProvCB(aID, aCertDuration) {
michael@0 60 let message = new IDDOMMessage({id: this.id});
michael@0 61 message.identity = aID;
michael@0 62 message.certDuration = aCertDuration;
michael@0 63 this._mm.sendAsyncMessage("Identity:IDP:CallBeginProvisioningCallback",
michael@0 64 message);
michael@0 65 },
michael@0 66
michael@0 67 doGenKeyPairCallback: function IDPPC_doGenKeyPairCallback(aPublicKey) {
michael@0 68 log("doGenKeyPairCallback");
michael@0 69 let message = new IDDOMMessage({id: this.id});
michael@0 70 message.publicKey = aPublicKey;
michael@0 71 this._mm.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message);
michael@0 72 },
michael@0 73
michael@0 74 doError: function(msg) {
michael@0 75 log("Provisioning ERROR: " + msg);
michael@0 76 }
michael@0 77 };
michael@0 78
michael@0 79 function IDPAuthenticationContext(aID, aOrigin, aTargetMM) {
michael@0 80 this._id = aID;
michael@0 81 this._origin = aOrigin;
michael@0 82 this._mm = aTargetMM;
michael@0 83 }
michael@0 84
michael@0 85 IDPAuthenticationContext.prototype = {
michael@0 86 get id() this._id,
michael@0 87 get origin() this._origin,
michael@0 88
michael@0 89 doBeginAuthenticationCallback: function IDPAC_doBeginAuthCB(aIdentity) {
michael@0 90 let message = new IDDOMMessage({id: this.id});
michael@0 91 message.identity = aIdentity;
michael@0 92 this._mm.sendAsyncMessage("Identity:IDP:CallBeginAuthenticationCallback",
michael@0 93 message);
michael@0 94 },
michael@0 95
michael@0 96 doError: function IDPAC_doError(msg) {
michael@0 97 log("Authentication ERROR: " + msg);
michael@0 98 }
michael@0 99 };
michael@0 100
michael@0 101 function RPWatchContext(aOptions, aTargetMM) {
michael@0 102 objectCopy(aOptions, this);
michael@0 103
michael@0 104 // id and origin are required
michael@0 105 if (! (this.id && this.origin)) {
michael@0 106 throw new Error("id and origin are required for RP watch context");
michael@0 107 }
michael@0 108
michael@0 109 // default for no loggedInUser is undefined, not null
michael@0 110 this.loggedInUser = aOptions.loggedInUser;
michael@0 111
michael@0 112 // Maybe internal. For hosted b2g identity shim.
michael@0 113 this._internal = aOptions._internal;
michael@0 114
michael@0 115 this._mm = aTargetMM;
michael@0 116 }
michael@0 117
michael@0 118 RPWatchContext.prototype = {
michael@0 119 doLogin: function RPWatchContext_onlogin(aAssertion, aMaybeInternalParams) {
michael@0 120 log("doLogin: " + this.id);
michael@0 121 let message = new IDDOMMessage({id: this.id, assertion: aAssertion});
michael@0 122 if (aMaybeInternalParams) {
michael@0 123 message._internalParams = aMaybeInternalParams;
michael@0 124 }
michael@0 125 this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogin", message);
michael@0 126 },
michael@0 127
michael@0 128 doLogout: function RPWatchContext_onlogout() {
michael@0 129 log("doLogout: " + this.id);
michael@0 130 let message = new IDDOMMessage({id: this.id});
michael@0 131 this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogout", message);
michael@0 132 },
michael@0 133
michael@0 134 doReady: function RPWatchContext_onready() {
michael@0 135 log("doReady: " + this.id);
michael@0 136 let message = new IDDOMMessage({id: this.id});
michael@0 137 this._mm.sendAsyncMessage("Identity:RP:Watch:OnReady", message);
michael@0 138 },
michael@0 139
michael@0 140 doCancel: function RPWatchContext_oncancel() {
michael@0 141 log("doCancel: " + this.id);
michael@0 142 let message = new IDDOMMessage({id: this.id});
michael@0 143 this._mm.sendAsyncMessage("Identity:RP:Watch:OnCancel", message);
michael@0 144 },
michael@0 145
michael@0 146 doError: function RPWatchContext_onerror(aMessage) {
michael@0 147 log("doError: " + this.id + ": " + JSON.stringify(aMessage));
michael@0 148 let message = new IDDOMMessage({id: this.id, message: aMessage});
michael@0 149 this._mm.sendAsyncMessage("Identity:RP:Watch:OnError", message);
michael@0 150 }
michael@0 151 };
michael@0 152
michael@0 153 this.DOMIdentity = {
michael@0 154 /*
michael@0 155 * When relying parties (RPs) invoke the watch() method, they can request
michael@0 156 * to use Firefox Accounts as their auth service or BrowserID (the default).
michael@0 157 * For each RP, we create an RPWatchContext to store the parameters given to
michael@0 158 * watch(), and to provide hooks to invoke the onlogin(), onlogout(), etc.
michael@0 159 * callbacks held in the nsDOMIdentity state.
michael@0 160 *
michael@0 161 * The serviceContexts map associates the window ID of the RP with the
michael@0 162 * context object. The mmContexts map associates a message manager with a
michael@0 163 * window ID. We use the mmContexts map when child-process-shutdown is
michael@0 164 * observed, and all we have is a message manager to identify the window in
michael@0 165 * question.
michael@0 166 */
michael@0 167 _serviceContexts: new Map(),
michael@0 168 _mmContexts: new Map(),
michael@0 169
michael@0 170 /*
michael@0 171 * Mockable, for testing
michael@0 172 */
michael@0 173 _mockIdentityService: null,
michael@0 174 get IdentityService() {
michael@0 175 if (this._mockIdentityService) {
michael@0 176 log("Using a mocked identity service");
michael@0 177 return this._mockIdentityService;
michael@0 178 }
michael@0 179 return IdentityService;
michael@0 180 },
michael@0 181
michael@0 182 /*
michael@0 183 * Create a new RPWatchContext, and update the context maps.
michael@0 184 */
michael@0 185 newContext: function(message, targetMM) {
michael@0 186 let context = new RPWatchContext(message, targetMM);
michael@0 187 this._serviceContexts.set(message.id, context);
michael@0 188 this._mmContexts.set(targetMM, message.id);
michael@0 189 return context;
michael@0 190 },
michael@0 191
michael@0 192 /*
michael@0 193 * Get the identity service used for an RP.
michael@0 194 *
michael@0 195 * @object message
michael@0 196 * A message received from an RP. Will include the id of the window
michael@0 197 * whence the message originated.
michael@0 198 *
michael@0 199 * Returns FirefoxAccounts or IdentityService
michael@0 200 */
michael@0 201 getService: function(message) {
michael@0 202 if (!this._serviceContexts.has(message.id)) {
michael@0 203 throw new Error("getService called before newContext for " + message.id);
michael@0 204 }
michael@0 205
michael@0 206 let context = this._serviceContexts.get(message.id);
michael@0 207 if (context.wantIssuer == "firefox-accounts") {
michael@0 208 if (Services.prefs.getPrefType(PREF_FXA_ENABLED) === Ci.nsIPrefBranch.PREF_BOOL
michael@0 209 && Services.prefs.getBoolPref(PREF_FXA_ENABLED)) {
michael@0 210 return FirefoxAccounts;
michael@0 211 }
michael@0 212 log("WARNING: Firefox Accounts is not enabled; Defaulting to BrowserID");
michael@0 213 }
michael@0 214 return this.IdentityService;
michael@0 215 },
michael@0 216
michael@0 217 /*
michael@0 218 * Get the RPWatchContext object for a given message manager.
michael@0 219 */
michael@0 220 getContextForMM: function(targetMM) {
michael@0 221 return this._serviceContexts.get(this._mmContexts.get(targetMM));
michael@0 222 },
michael@0 223
michael@0 224 hasContextForMM: function(targetMM) {
michael@0 225 return this._mmContexts.has(targetMM);
michael@0 226 },
michael@0 227
michael@0 228 /*
michael@0 229 * Delete the RPWatchContext object for a given message manager. Removes the
michael@0 230 * mapping both from _serviceContexts and _mmContexts.
michael@0 231 */
michael@0 232 deleteContextForMM: function(targetMM) {
michael@0 233 this._serviceContexts.delete(this._mmContexts.get(targetMM));
michael@0 234 this._mmContexts.delete(targetMM);
michael@0 235 },
michael@0 236
michael@0 237 // nsIMessageListener
michael@0 238 receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
michael@0 239 let msg = aMessage.json;
michael@0 240
michael@0 241 // Target is the frame message manager that called us and is
michael@0 242 // used to send replies back to the proper window.
michael@0 243 let targetMM = aMessage.target;
michael@0 244
michael@0 245 switch (aMessage.name) {
michael@0 246 // RP
michael@0 247 case "Identity:RP:Watch":
michael@0 248 this._watch(msg, targetMM);
michael@0 249 break;
michael@0 250 case "Identity:RP:Unwatch":
michael@0 251 this._unwatch(msg, targetMM);
michael@0 252 break;
michael@0 253 case "Identity:RP:Request":
michael@0 254 this._request(msg, targetMM);
michael@0 255 break;
michael@0 256 case "Identity:RP:Logout":
michael@0 257 this._logout(msg, targetMM);
michael@0 258 break;
michael@0 259 // IDP
michael@0 260 case "Identity:IDP:BeginProvisioning":
michael@0 261 this._beginProvisioning(msg, targetMM);
michael@0 262 break;
michael@0 263 case "Identity:IDP:GenKeyPair":
michael@0 264 this._genKeyPair(msg);
michael@0 265 break;
michael@0 266 case "Identity:IDP:RegisterCertificate":
michael@0 267 this._registerCertificate(msg);
michael@0 268 break;
michael@0 269 case "Identity:IDP:ProvisioningFailure":
michael@0 270 this._provisioningFailure(msg);
michael@0 271 break;
michael@0 272 case "Identity:IDP:BeginAuthentication":
michael@0 273 this._beginAuthentication(msg, targetMM);
michael@0 274 break;
michael@0 275 case "Identity:IDP:CompleteAuthentication":
michael@0 276 this._completeAuthentication(msg);
michael@0 277 break;
michael@0 278 case "Identity:IDP:AuthenticationFailure":
michael@0 279 this._authenticationFailure(msg);
michael@0 280 break;
michael@0 281 case "child-process-shutdown":
michael@0 282 // we receive child-process-shutdown if the appliction crashes,
michael@0 283 // including if it is crashed by the OS (killed for out-of-memory,
michael@0 284 // for example)
michael@0 285 this._childProcessShutdown(targetMM);
michael@0 286 break;
michael@0 287 }
michael@0 288 },
michael@0 289
michael@0 290 // nsIObserver
michael@0 291 observe: function DOMIdentity_observe(aSubject, aTopic, aData) {
michael@0 292 switch (aTopic) {
michael@0 293 case "xpcom-shutdown":
michael@0 294 this._unsubscribeListeners();
michael@0 295 Services.obs.removeObserver(this, "xpcom-shutdown");
michael@0 296 Services.ww.unregisterNotification(this);
michael@0 297 break;
michael@0 298 }
michael@0 299 },
michael@0 300
michael@0 301 messages: ["Identity:RP:Watch", "Identity:RP:Request", "Identity:RP:Logout",
michael@0 302 "Identity:IDP:BeginProvisioning", "Identity:IDP:ProvisioningFailure",
michael@0 303 "Identity:IDP:RegisterCertificate", "Identity:IDP:GenKeyPair",
michael@0 304 "Identity:IDP:BeginAuthentication",
michael@0 305 "Identity:IDP:CompleteAuthentication",
michael@0 306 "Identity:IDP:AuthenticationFailure",
michael@0 307 "Identity:RP:Unwatch",
michael@0 308 "child-process-shutdown"],
michael@0 309
michael@0 310 // Private.
michael@0 311 _init: function DOMIdentity__init() {
michael@0 312 Services.ww.registerNotification(this);
michael@0 313 Services.obs.addObserver(this, "xpcom-shutdown", false);
michael@0 314 this._subscribeListeners();
michael@0 315 },
michael@0 316
michael@0 317 _subscribeListeners: function DOMIdentity__subscribeListeners() {
michael@0 318 if (!ppmm) return;
michael@0 319 for (let message of this.messages) {
michael@0 320 ppmm.addMessageListener(message, this);
michael@0 321 }
michael@0 322 },
michael@0 323
michael@0 324 _unsubscribeListeners: function DOMIdentity__unsubscribeListeners() {
michael@0 325 for (let message of this.messages) {
michael@0 326 ppmm.removeMessageListener(message, this);
michael@0 327 }
michael@0 328 ppmm = null;
michael@0 329 },
michael@0 330
michael@0 331 _watch: function DOMIdentity__watch(message, targetMM) {
michael@0 332 log("DOMIdentity__watch: " + message.id);
michael@0 333 let context = this.newContext(message, targetMM);
michael@0 334 this.getService(message).RP.watch(context);
michael@0 335 },
michael@0 336
michael@0 337 _unwatch: function DOMIdentity_unwatch(message, targetMM) {
michael@0 338 log("DOMIDentity__unwatch: " + message.id);
michael@0 339 // If watch failed for some reason (e.g., exception thrown because RP did
michael@0 340 // not have the right callbacks, we don't want unwatch to throw, because it
michael@0 341 // will break the process of releasing the page's resources and leak
michael@0 342 // memory.
michael@0 343 try {
michael@0 344 this.getService(message).RP.unwatch(message.id, targetMM);
michael@0 345 } catch(ex) {
michael@0 346 log("ERROR: can't unwatch " + message.id + ": " + ex);
michael@0 347 }
michael@0 348 },
michael@0 349
michael@0 350 _request: function DOMIdentity__request(message) {
michael@0 351 this.getService(message).RP.request(message.id, message);
michael@0 352 },
michael@0 353
michael@0 354 _logout: function DOMIdentity__logout(message) {
michael@0 355 log("logout " + message + "\n");
michael@0 356 this.getService(message).RP.logout(message.id, message.origin, message);
michael@0 357 },
michael@0 358
michael@0 359 _childProcessShutdown: function DOMIdentity__childProcessShutdown(targetMM) {
michael@0 360 if (!this.hasContextForMM(targetMM)) {
michael@0 361 return;
michael@0 362 }
michael@0 363
michael@0 364 this.getContextForMM(targetMM).RP.childProcessShutdown(targetMM);
michael@0 365 this.deleteContextForMM(targetMM);
michael@0 366
michael@0 367 let options = makeMessageObject({messageManager: targetMM, id: null, origin: null});
michael@0 368 Services.obs.notifyObservers({wrappedJSObject: options}, "identity-child-process-shutdown", null);
michael@0 369 },
michael@0 370
michael@0 371 _beginProvisioning: function DOMIdentity__beginProvisioning(message, targetMM) {
michael@0 372 let context = new IDPProvisioningContext(message.id, message.origin,
michael@0 373 targetMM);
michael@0 374 this.getService(message).IDP.beginProvisioning(context);
michael@0 375 },
michael@0 376
michael@0 377 _genKeyPair: function DOMIdentity__genKeyPair(message) {
michael@0 378 this.getService(message).IDP.genKeyPair(message.id);
michael@0 379 },
michael@0 380
michael@0 381 _registerCertificate: function DOMIdentity__registerCertificate(message) {
michael@0 382 this.getService(message).IDP.registerCertificate(message.id, message.cert);
michael@0 383 },
michael@0 384
michael@0 385 _provisioningFailure: function DOMIdentity__provisioningFailure(message) {
michael@0 386 this.getService(message).IDP.raiseProvisioningFailure(message.id, message.reason);
michael@0 387 },
michael@0 388
michael@0 389 _beginAuthentication: function DOMIdentity__beginAuthentication(message, targetMM) {
michael@0 390 let context = new IDPAuthenticationContext(message.id, message.origin,
michael@0 391 targetMM);
michael@0 392 this.getService(message).IDP.beginAuthentication(context);
michael@0 393 },
michael@0 394
michael@0 395 _completeAuthentication: function DOMIdentity__completeAuthentication(message) {
michael@0 396 this.getService(message).IDP.completeAuthentication(message.id);
michael@0 397 },
michael@0 398
michael@0 399 _authenticationFailure: function DOMIdentity__authenticationFailure(message) {
michael@0 400 this.getService(message).IDP.cancelAuthentication(message.id);
michael@0 401 }
michael@0 402 };
michael@0 403
michael@0 404 // Object is initialized by nsIDService.js

mercurial