dom/identity/DOMIdentity.jsm

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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

mercurial