From 3a636705df0aa58d5a0264091294eb5d50e67b6f Mon Sep 17 00:00:00 2001 From: Thomas Steur Date: Mon, 6 Jul 2020 09:25:04 +1200 Subject: Don't set any cookies when no consent is given (#16173) * Better detection for cookies for browser plugins report * rebuilt piwik.js * improve comment * Add method to enable cookies * rebuilt piwik.js * fix test * no longer include cookie in fingerprint * only ignore cookies in fingerprint for IE * fix tests * fix test * tweak enablecookies * rebuilt piwik.js * send tracking request if needed when enable cookies * rebuilt piwik.js * tweak code * update docs * rebuilt piwik.js * Update Visit.php * fix tests * Don't set cookies unless consent given when consent is required * fix test * rebuilt piwik.js * add tests * add missing function * rebuilt piwik.js * fix jslint test --- js/piwik.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 5 deletions(-) (limited to 'js') diff --git a/js/piwik.js b/js/piwik.js index 6751051893..2ab5f9b9a8 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -960,7 +960,7 @@ if (typeof window.JSON === 'object' && typeof window.JSON.stringify === 'functio /*members Piwik, Matomo, encodeURIComponent, decodeURIComponent, getElementsByTagName, shift, unshift, piwikAsyncInit, piwikPluginAsyncInit, frameElement, self, hasFocus, createElement, appendChild, characterSet, charset, all, - addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies, enableCookies, + addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies, enableCookies, areCookiesEnabled, cookie, domain, readyState, documentElement, doScroll, title, text, contentWindow, postMessage, location, top, onerror, document, referrer, parent, links, href, protocol, name, GearsFactory, performance, mozPerformance, msPerformance, webkitPerformance, timing, requestStart, @@ -4372,7 +4372,6 @@ if (typeof window.Piwik !== 'object') { function setSiteId(siteId) { configTrackerSiteId = siteId; - setVisitorIdCookie(); } function sortObjectByKeys(value) { @@ -6803,6 +6802,31 @@ if (typeof window.Piwik !== 'object') { } }; + this.areCookiesEnabled = function () { + return !configCookiesDisabled; + }; + + /** + * Enables cookies if they were disabled previously + */ + this.enableCookies = function () { + if (configCookiesDisabled && !configDoNotTrack) { + configCookiesDisabled = false; + if (configTrackerSiteId && hasSentTrackingRequestYet) { + setVisitorIdCookie(); + + // sets attribution cookie, and updates visitorId in the backend + // because hasSentTrackingRequestYet=true we assume there might not be another tracking + // request within this page view so we trigger one ourselves. + // if no tracking request has been sent yet, we don't set the attribution cookie cause Matomo + // sets the cookie only when there is a tracking request. It'll be set if the user sends + // a tracking request afterwards + var request = getRequest('ping=1', null, 'ping'); + sendRequest(request, configTrackerPause); + } + } + }; + /** * Enables cookies if they were disabled previously */ @@ -7631,6 +7655,11 @@ if (typeof window.Piwik !== 'object') { this.requireConsent = function () { configConsentRequired = true; configHasConsent = this.hasRememberedConsent(); + if (!configHasConsent) { + // we won't call this.disableCookies() since we don't want to delete any cookies just yet + // user might call `setConsentGiven` next + configCookiesDisabled = true; + } // Piwik.addPlugin might not be defined at this point, we add the plugin directly also to make JSLint happy // We also want to make sure to define an unload listener for each tracker, not only one tracker. coreConsentCounter++; @@ -7648,10 +7677,16 @@ if (typeof window.Piwik !== 'object') { * Call this method once the user has given consent. This will cause all tracking requests from this * page view to be sent. Please note that the given consent won't be remembered across page views. If you * want to remember consent across page views, call {@link rememberConsentGiven()} instead. + * + * It will also automatically enable cookies if they were disabled previously. + * + * @param bool [enableCookies=true] Internal parameter. Defines whether cookies should be enabled or not. */ - this.setConsentGiven = function () { + this.setConsentGiven = function (enableCookies) { configHasConsent = true; + deleteCookie(CONSENT_REMOVED_COOKIE_NAME, configCookiePath, configCookieDomain); + var i, requestType; for (i = 0; i < consentRequestsQueue.length; i++) { requestType = typeof consentRequestsQueue[i]; @@ -7662,6 +7697,18 @@ if (typeof window.Piwik !== 'object') { } } consentRequestsQueue = []; + + // we need to enable cookies after sending the previous requests as it will make sure that we send + // a ping request if needed. Cookies are only set once we call `getRequest`. Above only calls sendRequest + // meaning no cookies will be created unless we called enableCookies after at least one request has been sent. + // this will cause a ping request to be sent that sets the cookies and also updates the newly generated visitorId + // on the server. + // If the user calls setConsentGiven before sending any tracking request (which usually is the case) then + // nothing will need to be done as it only enables cookies and the next tracking request will set the cookies + // etc. + if (!isDefined(enableCookies) || enableCookies) { + this.enableCookies(); + } }; /** @@ -7678,6 +7725,9 @@ if (typeof window.Piwik !== 'object') { * for all sites that match the configured cookieDomain and cookiePath. Depending on your website structure, * you may need to restrict or widen the scope of the cookie domain/path to ensure the consent is applied * to the sites you want. + * + * @param int hoursToExpire After how many hours the consent should expire. By default the consent is valid + * for 30 years unless cookies are deleted by the user or the browser prior to this */ this.rememberConsentGiven = function (hoursToExpire) { if (hoursToExpire) { @@ -7685,7 +7735,10 @@ if (typeof window.Piwik !== 'object') { } else { hoursToExpire = 30 * 365 * 24 * 60 * 60 * 1000; } - this.setConsentGiven(); + var enableCookies = true; + // we currently always enable cookies if we remember consent cause we don't store across requests whether + // cookies should be automatically enabled or not. + this.setConsentGiven(enableCookies); var now = new Date().getTime(); setCookie(CONSENT_COOKIE_NAME, now, hoursToExpire, configCookiePath, configCookieDomain, configCookieIsSecure); }; @@ -7722,7 +7775,11 @@ if (typeof window.Piwik !== 'object') { /** * Alias for rememberConsentGiven(). After calling this function, the current user will be tracked. */ - this.forgetUserOptOut = this.rememberConsentGiven; + this.forgetUserOptOut = function () { + // we can't automatically enable cookies here as we don't know if user actually gave consent for cookies + var enableCookies = false; + this.rememberConsentGiven(0, enableCookies); + }; Piwik.trigger('TrackerSetup', [this]); } -- cgit v1.2.3