Skip to content

Commit

Permalink
PROD-1125 1118 config overrides query cookie window (#4297)
Browse files Browse the repository at this point in the history
  • Loading branch information
eastandwestwind authored Oct 19, 2023
1 parent 7b15ac1 commit 8471288
Show file tree
Hide file tree
Showing 12 changed files with 510 additions and 40 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The types of changes are:

## [Unreleased](https://github.com/ethyca/fides/compare/2.22.0...main)
- Added support for 3 additional config variables in Fides.js: fidesEmbed, fidesDisableSaveApi, and fidesTcString [#4262](https://github.com/ethyca/fides/pull/4262)
- Added support for fidesEmbed, fidesDisableSaveApi, and fidesTcString to be passed into Fides.js via query param, cookie, or window object [#4297](https://github.com/ethyca/fides/pull/4297)

### Added
- Added a `FidesPreferenceToggled` event to Fides.js to track when user preferences change without being saved [#4253](https://github.com/ethyca/fides/pull/4253)
Expand Down
2 changes: 1 addition & 1 deletion clients/fides-js/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const GZIP_SIZE_ERROR_KB = 22; // fail build if bundle size exceeds this
const GZIP_SIZE_WARN_KB = 15; // log a warning if bundle size exceeds this

// TCF
const GZIP_SIZE_TCF_ERROR_KB = 41;
const GZIP_SIZE_TCF_ERROR_KB = 42;
const GZIP_SIZE_TCF_WARN_KB = 35;

const preactAliases = {
Expand Down
13 changes: 12 additions & 1 deletion clients/fides-js/src/fides-tcf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import { shopify } from "./integrations/shopify";

import {
FidesConfig,
FidesOptionOverrides,
OverrideOptions,
PrivacyExperience,
UserConsentPreference,
} from "./lib/consent-types";
Expand All @@ -60,6 +62,7 @@ import { generateFidesString, initializeCmpApi } from "./lib/tcf";
import {
getInitialCookie,
getInitialFides,
getOverrideFidesOptions,
initialize,
} from "./lib/initialize";
import type { Fides } from "./lib/initialize";
Expand All @@ -71,6 +74,7 @@ import {
hasSavedTcfPreferences,
isNewFidesCookie,
isPrivacyExperience,
tcfConsentCookieObjHasSomeConsentSet,
transformTcfPreferencesToCookieKeys,
transformUserPreferenceToBoolean,
} from "./fides";
Expand All @@ -96,6 +100,9 @@ declare global {
callback: (tcData: TCData, success: boolean) => void,
parameter?: number | string
) => void;
config: {
fides: OverrideOptions;
};
}
}

Expand Down Expand Up @@ -164,6 +171,10 @@ const updateCookie = async (
* Initialize the global Fides object with the given configuration values
*/
const init = async (config: FidesConfig) => {
const overrideOptions: Partial<FidesOptionOverrides> =
getOverrideFidesOptions();
// eslint-disable-next-line no-param-reassign
config.options = { ...config.options, ...overrideOptions };
const cookie = getInitialCookie(config);
if (config.options.fidesString) {
// If a fidesString is explicitly passed in, we override the associated cookie props, which are then used to
Expand All @@ -178,7 +189,7 @@ const init = async (config: FidesConfig) => {
config.options.debug
);
} else if (
cookie.tcf_consent &&
tcfConsentCookieObjHasSomeConsentSet(cookie.tcf_consent) &&
!cookie.fides_string &&
isPrivacyExperience(config.experience) &&
experienceIsValid(config.experience, config.options)
Expand Down
15 changes: 14 additions & 1 deletion clients/fides-js/src/fides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,20 @@ import {
buildCookieConsentForExperiences,
isNewFidesCookie,
} from "./lib/cookie";
import { FidesConfig, PrivacyExperience } from "./lib/consent-types";
import {
FidesConfig,
FidesOptionOverrides,
OverrideOptions,
PrivacyExperience,
} from "./lib/consent-types";

import { dispatchFidesEvent } from "./lib/events";

import {
initialize,
getInitialCookie,
getInitialFides,
getOverrideFidesOptions,
} from "./lib/initialize";
import type { Fides } from "./lib/initialize";

Expand All @@ -70,6 +76,9 @@ import { getConsentContext } from "./lib/consent-context";
declare global {
interface Window {
Fides: Fides;
config: {
fides: OverrideOptions;
};
}
}

Expand All @@ -95,6 +104,10 @@ const updateCookie = async (
* Initialize the global Fides object with the given configuration values
*/
const init = async (config: FidesConfig) => {
const overrideOptions: Partial<FidesOptionOverrides> =
getOverrideFidesOptions();
// eslint-disable-next-line no-param-reassign
config.options = { ...config.options, ...overrideOptions };
const cookie = getInitialCookie(config);
const initialFides = getInitialFides({ ...config, cookie });
if (initialFides) {
Expand Down
33 changes: 33 additions & 0 deletions clients/fides-js/src/lib/consent-constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { FidesOptionOverrides, OverrideOptions } from "./consent-types";

// Regex to validate a location string, which must:
// 1) Start with a 2-3 character country code (e.g. "US")
// 2) Optionally end with a 2-3 character region code (e.g. "CA")
// 3) Separated by a dash (e.g. "US-CA")
export const VALID_ISO_3166_LOCATION_REGEX = /^\w{2,3}(-\w{2,3})?$/;

export const FIDES_OVERRIDE_OPTIONS_VALIDATOR_MAP: {
fidesOption: keyof FidesOptionOverrides;
fidesOptionType: "string" | "boolean";
fidesOverrideKey: keyof OverrideOptions;
validationRegex: RegExp;
}[] = [
{
fidesOption: "fidesEmbed",
fidesOptionType: "boolean",
fidesOverrideKey: "fides_embed",
validationRegex: /^(true|false)$/,
},
{
fidesOption: "fidesDisableSaveApi",
fidesOptionType: "boolean",
fidesOverrideKey: "fides_disable_save_api",
validationRegex: /^(true|false)$/,
},
{
fidesOption: "fidesString",
fidesOptionType: "string",
fidesOverrideKey: "fides_string",
validationRegex: /(.*)/,
},
];
15 changes: 10 additions & 5 deletions clients/fides-js/src/lib/consent-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,16 @@ export type UserGeolocation = {
region?: string; // "NY"
};

// Regex to validate a location string, which must:
// 1) Start with a 2-3 character country code (e.g. "US")
// 2) Optionally end with a 2-3 character region code (e.g. "CA")
// 3) Separated by a dash (e.g. "US-CA")
export const VALID_ISO_3166_LOCATION_REGEX = /^\w{2,3}(-\w{2,3})?$/;
export type OverrideOptions = {
fides_string: string;
fides_disable_save_api: boolean;
fides_embed: boolean;
};

export type FidesOptionOverrides = Pick<
FidesOptions,
"fidesString" | "fidesDisableSaveApi" | "fidesEmbed"
>;

export enum ButtonType {
PRIMARY = "primary",
Expand Down
2 changes: 1 addition & 1 deletion clients/fides-js/src/lib/consent-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import {
PrivacyNotice,
UserConsentPreference,
UserGeolocation,
VALID_ISO_3166_LOCATION_REGEX,
} from "./consent-types";
import { EXPERIENCE_KEYS_WITH_PREFERENCES } from "./tcf/constants";
import { TCFPurposeConsentRecord } from "./tcf/types";
import { VALID_ISO_3166_LOCATION_REGEX } from "./consent-constants";

/**
* Wrapper around 'console.log' that only logs output when the 'debug' banner
Expand Down
20 changes: 19 additions & 1 deletion clients/fides-js/src/lib/cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from "./consent-utils";
import type { TcfCookieConsent, TcfSavePreferences } from "./tcf/types";
import { TCF_KEY_MAP } from "./tcf/constants";
import { TcfCookieKeyConsent } from "./tcf/types";

/**
* Store the user's consent preferences on the cookie, as key -> boolean pairs, e.g.
Expand Down Expand Up @@ -81,6 +82,17 @@ const CODEC: Types.CookieCodecConfig<string, string> = {
encodeValue: encodeURIComponent,
};

export const tcfConsentCookieObjHasSomeConsentSet = (
tcf_consent: TcfCookieConsent | undefined
): boolean => {
if (!tcf_consent) {
return false;
}
return Object.values(tcf_consent).some(
(val: TcfCookieKeyConsent) => Object.keys(val).length >= 0
);
};

/**
* Each cookie will be assigned an autogenerated user/device ID, to match user's
* consent preferences between the browser and the server. This is a randomly
Expand Down Expand Up @@ -118,6 +130,12 @@ export const makeFidesCookie = (consent?: CookieKeyConsent): FidesCookie => {
};
};

/**
* Retrieve cookie by name
*/
export const getCookieByName = (cookieName: string): string | undefined =>
getCookie(cookieName, CODEC);

/**
* Attempt to read, parse, and return the current Fides cookie from the browser.
* If one doesn't exist, make a new default cookie (including generating a new
Expand All @@ -138,7 +156,7 @@ export const getOrMakeFidesCookie = (
}

// Check for an existing cookie for this device
const cookieString = getCookie(CONSENT_COOKIE_NAME, CODEC);
const cookieString = getCookieByName(CONSENT_COOKIE_NAME);
if (!cookieString) {
debugLog(
debug,
Expand Down
32 changes: 32 additions & 0 deletions clients/fides-js/src/lib/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
CookieKeyConsent,
CookieMeta,
FidesCookie,
getCookieByName,
getOrMakeFidesCookie,
isNewFidesCookie,
makeConsentDefaultsLegacy,
Expand All @@ -20,6 +21,7 @@ import {
ConsentMethod,
EmptyExperience,
FidesConfig,
FidesOptionOverrides,
FidesOptions,
PrivacyExperience,
SaveConsentPreference,
Expand All @@ -40,6 +42,7 @@ import { updateConsentPreferences } from "./preferences";
import { resolveConsentValue } from "./consent-value";
import { initOverlay } from "./consent";
import { TcfCookieConsent } from "./tcf/types";
import { FIDES_OVERRIDE_OPTIONS_VALIDATOR_MAP } from "./consent-constants";

export type Fides = {
consent: CookieKeyConsent;
Expand Down Expand Up @@ -143,6 +146,35 @@ const automaticallyApplyGPCPreferences = ({
}
};

/**
* Gets and validates Fides override options provided through URL query params, cookie or window obj.
*/
export const getOverrideFidesOptions = (): Partial<FidesOptionOverrides> => {
const overrideOptions: Partial<FidesOptionOverrides> = {};
if (typeof window !== "undefined") {
const params = new URLSearchParams(document.location.search);
FIDES_OVERRIDE_OPTIONS_VALIDATOR_MAP.forEach(
({ fidesOption, fidesOptionType, fidesOverrideKey, validationRegex }) => {
// look for override options on URL query params, window obj, and cookie
const queryParamOverride: string | null = params.get(fidesOverrideKey);
const windowObjOverride: string | boolean | undefined = window.config
?.fides
? window.config?.fides[fidesOverrideKey]
: undefined;
const cookieOverride: string | undefined =
getCookieByName(fidesOverrideKey);
const value = queryParamOverride || windowObjOverride || cookieOverride;
if (value && validationRegex.test(value.toString())) {
// coerce to expected type in FidesOptions
overrideOptions[fidesOption] =
fidesOptionType === "string" ? value : JSON.parse(value.toString());
}
}
);
}
return overrideOptions;
};

/**
* Get the initial Fides cookie based on legacy consent values
* as well as any preferences stored in existing cookies
Expand Down
Loading

0 comments on commit 8471288

Please sign in to comment.