tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

IPPEnrollAndEntitleManager.sys.mjs (6372B)


      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
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 const lazy = {};
      6 
      7 ChromeUtils.defineESModuleGetters(lazy, {
      8  IPPStartupCache:
      9    "moz-src:///browser/components/ipprotection/IPPStartupCache.sys.mjs",
     10  IPProtectionService:
     11    "moz-src:///browser/components/ipprotection/IPProtectionService.sys.mjs",
     12  IPPSignInWatcher:
     13    "moz-src:///browser/components/ipprotection/IPPSignInWatcher.sys.mjs",
     14 });
     15 
     16 const LOG_PREF = "browser.ipProtection.log";
     17 
     18 ChromeUtils.defineLazyGetter(lazy, "logConsole", function () {
     19  return console.createInstance({
     20    prefix: "IPPEnrollAndEntitleManager",
     21    maxLogLevel: Services.prefs.getBoolPref(LOG_PREF, false) ? "Debug" : "Warn",
     22  });
     23 });
     24 
     25 /**
     26 * This class manages the enrolling and entitlement.
     27 */
     28 class IPPEnrollAndEntitleManagerSingleton extends EventTarget {
     29  #runningPromise = null;
     30 
     31  #entitlement = null;
     32 
     33  constructor() {
     34    super();
     35 
     36    this.handleEvent = this.#handleEvent.bind(this);
     37  }
     38 
     39  init() {
     40    // We will use data from the cache until we are fully functional. Then we
     41    // will recompute the state in `initOnStartupCompleted`.
     42    this.#entitlement = lazy.IPPStartupCache.entitlement;
     43 
     44    lazy.IPPSignInWatcher.addEventListener(
     45      "IPPSignInWatcher:StateChanged",
     46      this.handleEvent
     47    );
     48  }
     49 
     50  initOnStartupCompleted() {
     51    if (!lazy.IPPSignInWatcher.isSignedIn) {
     52      return;
     53    }
     54 
     55    try {
     56      // This bit must be async because we want to trigger the updateState at
     57      // the end of the rest of the initialization.
     58      lazy.IPProtectionService.guardian
     59        .isLinkedToGuardian(/* only cache: */ true)
     60        .then(
     61          async isLinked => {
     62            if (isLinked) {
     63              const { status, entitlement } =
     64                await lazy.IPProtectionService.guardian.fetchUserInfo();
     65              if (status === 200) {
     66                this.#setEntitlement(entitlement);
     67                return;
     68              }
     69            }
     70            this.#setEntitlement(null);
     71          },
     72          () => {
     73            // In case we were using cached values, it's time to reset them.
     74            this.#setEntitlement(null);
     75          }
     76        );
     77    } catch (_) {
     78      // In case we were using cached values, it's time to reset them.
     79      this.#setEntitlement(null);
     80    }
     81  }
     82 
     83  uninit() {
     84    lazy.IPPSignInWatcher.removeEventListener(
     85      "IPPSignInWatcher:StateChanged",
     86      this.handleEvent
     87    );
     88 
     89    this.#entitlement = null;
     90  }
     91 
     92  #handleEvent(_event) {
     93    if (!lazy.IPPSignInWatcher.isSignedIn) {
     94      this.#setEntitlement(null);
     95      return;
     96    }
     97 
     98    this.maybeEnrollAndEntitle();
     99  }
    100 
    101  maybeEnrollAndEntitle(forceRefetch = false) {
    102    if (this.#runningPromise) {
    103      return this.#runningPromise;
    104    }
    105 
    106    if (this.#entitlement && !forceRefetch) {
    107      return Promise.resolve({ isEnrolledAndEntitled: true });
    108    }
    109 
    110    const enrollAndEntitle = async () => {
    111      const data =
    112        await IPPEnrollAndEntitleManagerSingleton.#maybeEnrollAndEntitle();
    113      if (!data.entitlement) {
    114        // Unset the entitlement if not available.
    115        this.#setEntitlement(null);
    116        return { isEnrolledAndEntitled: false, error: data.error };
    117      }
    118 
    119      this.#setEntitlement(data.entitlement);
    120      return { isEnrolledAndEntitled: true };
    121    };
    122 
    123    this.#runningPromise = enrollAndEntitle().finally(() => {
    124      this.#runningPromise = null;
    125    });
    126 
    127    return this.#runningPromise;
    128  }
    129 
    130  // This method is static because we don't want to change the internal state
    131  // of the singleton.
    132  static async #maybeEnrollAndEntitle() {
    133    let isLinked = false;
    134    try {
    135      isLinked = await lazy.IPProtectionService.guardian.isLinkedToGuardian(
    136        /* only cache: */ false
    137      );
    138    } catch (error) {
    139      // If not linked, it's not an issue.
    140    }
    141 
    142    if (isLinked) {
    143      // Linked does not mean enrolled: it could be that the link comes from a
    144      // previous MozillaVPN subscription. Let's see if `fetchUserInfo` is able
    145      // to obtain the entitlement.
    146      const { status, entitlement } =
    147        await lazy.IPProtectionService.guardian.fetchUserInfo();
    148      if (status === 200) {
    149        return { entitlement };
    150      }
    151    }
    152 
    153    try {
    154      const enrollment = await lazy.IPProtectionService.guardian.enroll();
    155      if (!enrollment?.ok) {
    156        return { entitlement: null, error: enrollment?.error };
    157      }
    158    } catch (error) {
    159      return { enrollment: null, error: error?.message };
    160    }
    161 
    162    const { status, entitlement, error } =
    163      await lazy.IPProtectionService.guardian.fetchUserInfo();
    164    lazy.logConsole.debug("Entitlement:", { status, entitlement, error });
    165 
    166    // If we see an error during the READY state, let's trigger an error state.
    167    if (error || !entitlement || status != 200) {
    168      return { entitlement: null, error: error || `Status: ${status}` };
    169    }
    170 
    171    return { entitlement };
    172  }
    173 
    174  #setEntitlement(entitlement) {
    175    this.#entitlement = entitlement;
    176    lazy.IPPStartupCache.storeEntitlement(this.#entitlement);
    177 
    178    lazy.IPProtectionService.updateState();
    179 
    180    this.dispatchEvent(
    181      new CustomEvent("IPPEnrollAndEntitleManager:StateChanged", {
    182        bubbles: true,
    183        composed: true,
    184      })
    185    );
    186  }
    187 
    188  get isEnrolledAndEntitled() {
    189    return !!this.#entitlement;
    190  }
    191 
    192  /**
    193   * Checks if a user has upgraded.
    194   *
    195   * @returns {boolean}
    196   */
    197  get hasUpgraded() {
    198    return this.#entitlement?.subscribed;
    199  }
    200 
    201  /**
    202   * Checks if the entitlement exists and it contains a UUID
    203   */
    204  get hasEntitlementUid() {
    205    return !!this.#entitlement?.uid;
    206  }
    207 
    208  /**
    209   * Checks if we have the entitlement
    210   */
    211  get hasEntitlement() {
    212    return !!this.#entitlement;
    213  }
    214 
    215  /**
    216   * Checks if we're running the Alpha variant based on
    217   * available features
    218   */
    219  get isAlpha() {
    220    return (
    221      !this.#entitlement?.autostart &&
    222      !this.#entitlement?.website_inclusion &&
    223      !this.#entitlement?.location_controls
    224    );
    225  }
    226 
    227  async refetchEntitlement() {
    228    await this.maybeEnrollAndEntitle(true);
    229  }
    230 
    231  resetEntitlement() {
    232    this.#setEntitlement(null);
    233  }
    234 }
    235 
    236 const IPPEnrollAndEntitleManager = new IPPEnrollAndEntitleManagerSingleton();
    237 
    238 export { IPPEnrollAndEntitleManager };